diff --git a/README.md b/README.md index f7eb9509..a89d917d 100644 --- a/README.md +++ b/README.md @@ -141,14 +141,14 @@ Binaries will be published once the codebase reaches a stable condition. |User|Contribution Amount| |--|--| -| [thrasher-](https://github.com/thrasher-) | 640 | +| [thrasher-](https://github.com/thrasher-) | 641 | | [shazbert](https://github.com/shazbert) | 191 | | [gloriousCode](https://github.com/gloriousCode) | 169 | -| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 48 | -| [xtda](https://github.com/xtda) | 43 | +| [dependabot-preview[bot]](https://github.com/apps/dependabot-preview) | 51 | +| [xtda](https://github.com/xtda) | 45 | | [ermalguni](https://github.com/ermalguni) | 14 | | [vadimzhukck](https://github.com/vadimzhukck) | 10 | -| [MadCozBadd](https://github.com/MadCozBadd) | 8 | +| [MadCozBadd](https://github.com/MadCozBadd) | 9 | | [140am](https://github.com/140am) | 8 | | [marcofranssen](https://github.com/marcofranssen) | 8 | | [dackroyd](https://github.com/dackroyd) | 5 | diff --git a/cmd/config/config.go b/cmd/config/config.go index b1e28e48..640caf64 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -1,6 +1,7 @@ package main import ( + "encoding/json" "flag" "io/ioutil" "log" @@ -49,9 +50,9 @@ func main() { if !config.ConfirmECS(fileData) && !encrypt { var result interface{} - errf := config.ConfirmConfigJSON(fileData, result) + errf := json.Unmarshal(fileData, &result) if errf != nil { - log.Fatal("File isn't in JSON format") + log.Fatal(errf) } log.Println("File is already decrypted. Encrypting..") encrypt = true diff --git a/cmd/exchange_template/exchange_template.go b/cmd/exchange_template/exchange_template.go index 770167f0..be8da2df 100644 --- a/cmd/exchange_template/exchange_template.go +++ b/cmd/exchange_template/exchange_template.go @@ -15,7 +15,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/core" "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) const ( @@ -147,9 +146,6 @@ func makeExchange(exch *exchange) error { newExchConfig.API.Credentials.Key = "Key" newExchConfig.API.Credentials.Secret = "Secret" newExchConfig.CurrencyPairs = ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, UseGlobalFormat: true, RequestFormat: ¤cy.PairFormat{ Uppercase: true, diff --git a/cmd/exchange_template/wrapper_file.tmpl b/cmd/exchange_template/wrapper_file.tmpl index 80b24037..e708df43 100644 --- a/cmd/exchange_template/wrapper_file.tmpl +++ b/cmd/exchange_template/wrapper_file.tmpl @@ -17,7 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -30,13 +30,10 @@ func ({{.Variable}} *{{.CapitalName}}) GetDefaultConfig() (*config.ExchangeConfi exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout exchCfg.BaseCurrencies = {{.Variable}}.BaseCurrencies - err := {{.Variable}}.SetupDefaults(exchCfg) - if err != nil { - return nil, err - } + {{.Variable}}.SetupDefaults(exchCfg) if {{.Variable}}.Features.Supports.RESTCapabilities.AutoPairUpdates { - err = {{.Variable}}.UpdateTradablePairs(true) + err := {{.Variable}}.UpdateTradablePairs(true) if err != nil { return nil, err } @@ -51,20 +48,46 @@ func ({{.Variable}} *{{.CapitalName}}) SetDefaults() { {{.Variable}}.Verbose = true {{.Variable}}.API.CredentialsValidator.RequiresKey = true {{.Variable}}.API.CredentialsValidator.RequiresSecret = true - {{.Variable}}.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "-", - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "-", - }, + + // If using only one pair format for request and configuration, across all + // supported asset types either SPOT and FUTURES etc. You can use the + // example below: + + // Request format denotes what the pair as a string will be, when you send + // a request to an exchange. + requestFmt := ¤cy.PairFormat{/*Set pair request formatting details here for e.g.*/ Uppercase: true, Delimiter: ":"} + // Config format denotes what the pair as a string will be, when saved to + // the config.json file. + configFmt := ¤cy.PairFormat{/*Set pair request formatting details here*/} + err := {{.Variable}}.SetGlobalPairsManager(requestFmt, configFmt, /*multiple assets can be set here using the asset package ie asset.Spot*/) + if err != nil { + log.Errorln(log.ExchangeSys, err) } + + // If assets require multiple differences in formating for request and + // configuration, another exchange method can be be used e.g. futures + // contracts require a dash as a delimiter rather than an underscore. You + // can use this example below: + + fmt1 := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{Uppercase: true}, + ConfigFormat: ¤cy.PairFormat{Uppercase: true}, + } + + fmt2 := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{Uppercase: true}, + ConfigFormat: ¤cy.PairFormat{Uppercase: true, Delimiter: ":"}, + } + + err = {{.Variable}}.StoreAssetPairFormat(asset.Spot, fmt1) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + err = {{.Variable}}.StoreAssetPairFormat(asset.Margin, fmt2) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + // Fill out the capabilities/features that the exchange supports {{.Variable}}.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ @@ -91,7 +114,7 @@ func ({{.Variable}} *{{.CapitalName}}) SetDefaults() { {{.Variable}}.API.Endpoints.URLDefault = {{.Name}}APIURL {{.Variable}}.API.Endpoints.URL = {{.Variable}}.API.Endpoints.URLDefault - {{.Variable}}.Websocket = wshandler.New() + {{.Variable}}.Websocket = stream.New() {{.Variable}}.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit {{.Variable}}.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout {{.Variable}}.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -104,15 +127,12 @@ func ({{.Variable}} *{{.CapitalName}}) Setup(exch *config.ExchangeConfig) error return nil } - err := {{.Variable}}.SetupDefaults(exch) - if err != nil { - return err - } + {{.Variable}}.SetupDefaults(exch) // If websocket is supported, please fill out the following /* err = {{.Variable}}.Websocket.Setup( - &wshandler.WebsocketSetup{ + &stream.WebsocketSetup{ Enabled: exch.Features.Enabled.Websocket, Verbose: exch.Verbose, AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, @@ -129,7 +149,7 @@ func ({{.Variable}} *{{.CapitalName}}) Setup(exch *config.ExchangeConfig) error return err } - {{.Variable}}.WebsocketConn = &wshandler.WebsocketConnection{ + {{.Variable}}.WebsocketConn = &stream.WebsocketConnection{ ExchangeName: {{.Variable}}.Name, URL: {{.Variable}}.Websocket.GetWebsocketURL(), ProxyURL: {{.Variable}}.Websocket.GetProxyAddress(), @@ -195,8 +215,13 @@ func ({{.Variable}} *{{.CapitalName}}) UpdateTradablePairs(forceUpdate bool) err if err != nil { return err } - return {{.Variable}}.UpdatePairs(currency.NewPairsFromStrings(pairs), - asset.Spot, false, forceUpdate) + + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + return {{.Variable}}.UpdatePairs(p, asset.Spot, false, forceUpdate) } @@ -356,11 +381,6 @@ func ({{.Variable}} *{{.CapitalName}}) WithdrawFiatFundsToInternationalBank(with return nil, common.ErrNotYetImplemented } -// GetWebsocket returns a pointer to the exchange websocket -func ({{.Variable}} *{{.CapitalName}}) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrNotYetImplemented -} - // GetActiveOrders retrieves any orders that are active/open func ({{.Variable}} *{{.CapitalName}}) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) { return nil, common.ErrNotYetImplemented @@ -377,30 +397,6 @@ func ({{.Variable}} *{{.CapitalName}}) GetFeeByType(feeBuilder *exchange.FeeBuil return 0, common.ErrNotYetImplemented } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func ({{.Variable}} *{{.CapitalName}}) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - {{.Variable}}.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func ({{.Variable}} *{{.CapitalName}}) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - {{.Variable}}.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func ({{.Variable}} *{{.CapitalName}}) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrNotYetImplemented -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func ({{.Variable}} *{{.CapitalName}}) AuthenticateWebsocket() error { - return common.ErrNotYetImplemented -} - // ValidateCredentials validates current credentials used for wrapper func ({{.Variable}} *{{.CapitalName}}) ValidateCredentials() error { _, err := {{.Variable}}.UpdateAccountInfo() diff --git a/cmd/exchange_wrapper_issues/main.go b/cmd/exchange_wrapper_issues/main.go index 62ae820d..5cb63322 100644 --- a/cmd/exchange_wrapper_issues/main.go +++ b/cmd/exchange_wrapper_issues/main.go @@ -296,10 +296,16 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) switch { case currencyPairOverride != "": - p = currency.NewPairFromString(currencyPairOverride) + var err error + p, err = currency.NewPairFromString(currencyPairOverride) + if err != nil { + log.Printf("%v Encountered error: '%v'", base.GetName(), err) + continue + } case len(base.Config.CurrencyPairs.Pairs[assetTypes[i]].Enabled) == 0: if len(base.Config.CurrencyPairs.Pairs[assetTypes[i]].Available) == 0 { - log.Printf("%v has no enabled or available currencies. Skipping", base.GetName()) + log.Printf("%v has no enabled or available currencies. Skipping", + base.GetName()) continue } p = base.Config.CurrencyPairs.Pairs[assetTypes[i]].Available.GetRandomPair() @@ -308,8 +314,8 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config) } responseContainer := ExchangeAssetPairResponses{ - AssetType: assetTypes[i], - CurrencyPair: p, + AssetType: assetTypes[i], + Pair: p, } log.Printf("Setup config for %v %v %v", base.GetName(), assetTypes[i], p) @@ -858,7 +864,7 @@ func outputToConsole(exchangeResponses []ExchangeResponses) { log.Printf("%v Result: %v", exchangeResponses[i].ExchangeName, k) log.Printf("Function:\t%v", exchangeResponses[i].AssetPairResponses[j].EndpointResponses[k].Function) log.Printf("AssetType:\t%v", exchangeResponses[i].AssetPairResponses[j].AssetType) - log.Printf("Currency:\t%v\n", exchangeResponses[i].AssetPairResponses[j].CurrencyPair) + log.Printf("Currency:\t%v\n", exchangeResponses[i].AssetPairResponses[j].Pair) log.Printf("Wrapper Params:\t%s\n", exchangeResponses[i].AssetPairResponses[j].EndpointResponses[k].SentParams) if exchangeResponses[i].AssetPairResponses[j].EndpointResponses[k].Error != "" { totalErrors++ diff --git a/cmd/exchange_wrapper_issues/report.tmpl b/cmd/exchange_wrapper_issues/report.tmpl index 2fcede34..ad380698 100644 --- a/cmd/exchange_wrapper_issues/report.tmpl +++ b/cmd/exchange_wrapper_issues/report.tmpl @@ -69,10 +69,10 @@ {{ if eq $length 1 }} {{ else }}
-

{{ $assetPairResponses.AssetType }} {{ $assetPairResponses.CurrencyPair }} Results

+

{{ $assetPairResponses.AssetType }} {{ $assetPairResponses.Pair}} Results

@@ -108,18 +108,18 @@ {{ $assetPairResponses.AssetType }} - {{ $assetPairResponses.CurrencyPair }} + {{ $assetPairResponses.Pair}} {{ $endpointResponse.Function }} -
{{ $endpointResponse.SentParams | printf "%s" }} @@ -131,11 +131,11 @@ -
{{ $endpointResponse.Response | printf "%s" }} diff --git a/cmd/exchange_wrapper_issues/types.go b/cmd/exchange_wrapper_issues/types.go index 08b7e467..ebf518fe 100644 --- a/cmd/exchange_wrapper_issues/types.go +++ b/cmd/exchange_wrapper_issues/types.go @@ -58,7 +58,7 @@ type ExchangeResponses struct { type ExchangeAssetPairResponses struct { ErrorCount int64 `json:"errorCount"` AssetType asset.Item `json:"asset"` - CurrencyPair currency.Pair `json:"currency"` + Pair currency.Pair `json:"currency"` EndpointResponses []EndpointResponse `json:"responses"` } diff --git a/cmd/gctcli/commands.go b/cmd/gctcli/commands.go index cef30dae..f9374ca9 100644 --- a/cmd/gctcli/commands.go +++ b/cmd/gctcli/commands.go @@ -7,9 +7,7 @@ import ( "io/ioutil" "math" "os" - "os/exec" "path/filepath" - "runtime" "strconv" "strings" "time" @@ -566,7 +564,11 @@ func getTicker(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetTicker(context.Background(), &gctrpc.GetTickerRequest{ @@ -679,7 +681,11 @@ func getOrderbook(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetOrderbook(context.Background(), &gctrpc.GetOrderbookRequest{ @@ -1199,7 +1205,11 @@ func getOrders(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetOrders(context.Background(), &gctrpc.GetOrdersRequest{ Exchange: exchangeName, @@ -1407,7 +1417,11 @@ func submitOrder(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.SubmitOrder(context.Background(), &gctrpc.SubmitOrderRequest{ Exchange: exchangeName, @@ -1516,7 +1530,11 @@ func simulateOrder(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.SimulateOrder(context.Background(), &gctrpc.SimulateOrderRequest{ Exchange: exchangeName, @@ -1618,7 +1636,11 @@ func whaleBomb(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.WhaleBomb(context.Background(), &gctrpc.WhaleBombRequest{ Exchange: exchangeName, @@ -1750,7 +1772,11 @@ func cancelOrder(c *cli.Context) error { if !validPair(currencyPair) { return errInvalidPair } - p = currency.NewPairDelimiter(currencyPair, pairDelimiter) + var err error + p, err = currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } } conn, err := setupClient() @@ -1985,7 +2011,11 @@ func addEvent(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } + client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.AddEvent(context.Background(), &gctrpc.AddEventRequest{ Exchange: exchangeName, @@ -2776,249 +2806,6 @@ func setLoggerDetails(c *cli.Context) error { return nil } -var getExchangePairsCommand = cli.Command{ - Name: "getexchangepairs", - Usage: "gets an exchanges supported currency pairs (available and enabled) plus asset types", - ArgsUsage: " ", - Action: getExchangePairs, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "exchange", - Usage: "the exchange to list of the currency pairs of", - }, - cli.StringFlag{ - Name: "asset", - Usage: "the asset type to filter by", - }, - }, -} - -func getExchangePairs(c *cli.Context) error { - if c.NArg() == 0 && c.NumFlags() == 0 { - cli.ShowCommandHelp(c, "getexchangepairs") - return nil - } - - var exchange string - var asset string - - if c.IsSet("exchange") { - exchange = c.String("exchange") - } else { - exchange = c.Args().First() - } - - if !validExchange(exchange) { - return errInvalidExchange - } - - if c.IsSet("asset") { - asset = c.String("asset") - } else { - asset = c.Args().Get(1) - } - - asset = strings.ToLower(asset) - if !validAsset(asset) { - return errInvalidAsset - } - - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - - client := gctrpc.NewGoCryptoTraderClient(conn) - result, err := client.GetExchangePairs(context.Background(), - &gctrpc.GetExchangePairsRequest{ - Exchange: exchange, - Asset: asset, - }, - ) - if err != nil { - return err - } - jsonOutput(result) - return nil -} - -var enableExchangePairCommand = cli.Command{ - Name: "enableexchangepair", - Usage: "enables an exchange currency pair", - ArgsUsage: " ", - Action: enableExchangePair, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "exchange", - Usage: "the exchange to enable the currency pair for", - }, - cli.StringFlag{ - Name: "pair", - Usage: "the currency pair to enable", - }, - cli.StringFlag{ - Name: "asset", - Usage: "the asset type to enable the currency pair for", - }, - }, -} - -func enableExchangePair(c *cli.Context) error { - if c.NArg() == 0 && c.NumFlags() == 0 { - cli.ShowCommandHelp(c, "enableexchangepair") - return nil - } - - var exchange string - var pair string - var asset string - - if c.IsSet("exchange") { - exchange = c.String("exchange") - } else { - 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 { - asset = c.Args().Get(2) - } - - asset = strings.ToLower(asset) - if !validAsset(asset) { - return errInvalidAsset - } - - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - - p := currency.NewPairDelimiter(pair, pairDelimiter) - client := gctrpc.NewGoCryptoTraderClient(conn) - result, err := client.EnableExchangePair(context.Background(), - &gctrpc.ExchangePairRequest{ - Exchange: exchange, - Pair: &gctrpc.CurrencyPair{ - Delimiter: p.Delimiter, - Base: p.Base.String(), - Quote: p.Quote.String(), - }, - AssetType: asset, - }, - ) - if err != nil { - return err - } - jsonOutput(result) - return nil -} - -var disableExchangePairCommand = cli.Command{ - Name: "disableexchangepair", - Usage: "disables a previously enabled exchange currency pair", - ArgsUsage: " ", - Action: disableExchangePair, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "exchange", - Usage: "the exchange to disable the currency pair for", - }, - cli.StringFlag{ - Name: "pair", - Usage: "the currency pair to disable", - }, - cli.StringFlag{ - Name: "asset", - Usage: "the asset type to disable the currency pair for", - }, - }, -} - -func disableExchangePair(c *cli.Context) error { - if c.NArg() == 0 && c.NumFlags() == 0 { - cli.ShowCommandHelp(c, "disableexchangepair") - return nil - } - - var exchange string - var pair string - var asset string - - if c.IsSet("exchange") { - exchange = c.String("exchange") - } else { - 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 { - asset = c.Args().Get(2) - } - - asset = strings.ToLower(asset) - if !validAsset(asset) { - return errInvalidAsset - } - - conn, err := setupClient() - if err != nil { - return err - } - defer conn.Close() - - p := currency.NewPairDelimiter(pair, pairDelimiter) - client := gctrpc.NewGoCryptoTraderClient(conn) - result, err := client.DisableExchangePair(context.Background(), - &gctrpc.ExchangePairRequest{ - Exchange: exchange, - Pair: &gctrpc.CurrencyPair{ - Delimiter: p.Delimiter, - Base: p.Base.String(), - Quote: p.Quote.String(), - }, - AssetType: asset, - }, - ) - if err != nil { - return err - } - jsonOutput(result) - return nil -} - var getOrderbookStreamCommand = cli.Command{ Name: "getorderbookstream", Usage: "gets the orderbook stream for a specific currency pair and exchange", @@ -3088,7 +2875,10 @@ func getOrderbookStream(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(pair, pairDelimiter) + p, err := currency.NewPairDelimiter(pair, pairDelimiter) + if err != nil { + return err + } client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetOrderbookStream(context.Background(), @@ -3296,7 +3086,10 @@ func getTickerStream(c *cli.Context) error { } defer conn.Close() - p := currency.NewPairDelimiter(pair, pairDelimiter) + p, err := currency.NewPairDelimiter(pair, pairDelimiter) + if err != nil { + return err + } client := gctrpc.NewGoCryptoTraderClient(conn) result, err := client.GetTickerStream(context.Background(), @@ -3410,19 +3203,6 @@ func getExchangeTickerStream(c *cli.Context) error { } } -func clearScreen() error { - switch runtime.GOOS { - case "windows": - cmd := exec.Command("cmd", "/c", "cls") - cmd.Stdout = os.Stdout - return cmd.Run() - default: - cmd := exec.Command("clear") - cmd.Stdout = os.Stdout - return cmd.Run() - } -} - var getAuditEventCommand = cli.Command{ Name: "getauditevent", Usage: "gets audit events matching query parameters", @@ -3529,8 +3309,8 @@ func getAuditEvent(c *cli.Context) error { var uuid, filename, path string var gctScriptCommand = cli.Command{ - Name: "gctscript", - Usage: "execute gctscript command", + Name: "script", + Usage: "execute scripting management command", ArgsUsage: " ", Subcommands: []cli.Command{ { @@ -4022,7 +3802,10 @@ func getHistoricCandles(c *cli.Context) error { if !validPair(currencyPair) { return errInvalidPair } - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } var assetType string if c.IsSet("asset") { @@ -4038,7 +3821,6 @@ func getHistoricCandles(c *cli.Context) error { if c.IsSet("rangesize") { candleRangeSize = c.Int64("rangesize") } else if c.Args().Get(3) != "" { - var err error candleRangeSize, err = strconv.ParseInt(c.Args().Get(3), 10, 64) if err != nil { return err @@ -4048,7 +3830,6 @@ func getHistoricCandles(c *cli.Context) error { if c.IsSet("granularity") { candleGranularity = c.Int64("granularity") } else if c.Args().Get(4) != "" { - var err error candleGranularity, err = strconv.ParseInt(c.Args().Get(4), 10, 64) if err != nil { return err @@ -4151,7 +3932,11 @@ func getHistoricCandlesExtended(c *cli.Context) error { if !validPair(currencyPair) { return errInvalidPair } - p := currency.NewPairDelimiter(currencyPair, pairDelimiter) + + p, err := currency.NewPairDelimiter(currencyPair, pairDelimiter) + if err != nil { + return err + } var assetType string if c.IsSet("asset") { @@ -4167,7 +3952,6 @@ func getHistoricCandlesExtended(c *cli.Context) error { if c.IsSet("interval") { candleGranularity = c.Int64("interval") } else if c.Args().Get(3) != "" { - var err error candleGranularity, err = strconv.ParseInt(c.Args().Get(3), 10, 64) if err != nil { return err diff --git a/cmd/gctcli/helpers.go b/cmd/gctcli/helpers.go new file mode 100644 index 00000000..8a1872d9 --- /dev/null +++ b/cmd/gctcli/helpers.go @@ -0,0 +1,20 @@ +package main + +import ( + "os" + "os/exec" + "runtime" +) + +func clearScreen() error { + switch runtime.GOOS { + case "windows": + cmd := exec.Command("cmd", "/c", "cls") + cmd.Stdout = os.Stdout + return cmd.Run() + default: + cmd := exec.Command("clear") + cmd.Stdout = os.Stdout + return cmd.Run() + } +} diff --git a/cmd/gctcli/main.go b/cmd/gctcli/main.go index 01f2b214..7b08ba54 100644 --- a/cmd/gctcli/main.go +++ b/cmd/gctcli/main.go @@ -127,9 +127,7 @@ func main() { withdrawalRequestCommand, getLoggerDetailsCommand, setLoggerDetailsCommand, - getExchangePairsCommand, - enableExchangePairCommand, - disableExchangePairCommand, + exchangePairManagerCommand, getOrderbookStreamCommand, getExchangeOrderbookStreamCommand, getTickerStreamCommand, @@ -138,6 +136,7 @@ func main() { getHistoricCandlesCommand, getHistoricCandlesExtendedCommand, gctScriptCommand, + websocketManagerCommand, } err := app.Run(os.Args) diff --git a/cmd/gctcli/pair_management.go b/cmd/gctcli/pair_management.go new file mode 100644 index 00000000..148ae0fd --- /dev/null +++ b/cmd/gctcli/pair_management.go @@ -0,0 +1,456 @@ +package main + +import ( + "context" + "strings" + + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/gctrpc" + "github.com/urfave/cli" +) + +var exchangePairManagerCommand = cli.Command{ + Name: "pair", + Usage: "execute exchange pair management command", + ArgsUsage: " ", + Subcommands: []cli.Command{ + { + Name: "get", + Usage: "returns all enabled and available pairs by asset type", + ArgsUsage: "", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.StringFlag{ + Name: "asset", + Usage: "asset", + }, + }, + Action: getExchangePairs, + }, + { + Name: "disableasset", + Usage: "disables asset type", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.StringFlag{ + Name: "asset", + Usage: "asset", + }, + }, + Action: enableDisableExchangeAsset, + }, + { + Name: "enableasset", + Usage: "enables asset type", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.StringFlag{ + Name: "asset", + Usage: "asset", + }, + cli.BoolTFlag{ + Name: "enable", + Hidden: true, + }, + }, + Action: enableDisableExchangeAsset, + }, + { + Name: "disable", + Usage: "disable pairs by asset type", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.StringFlag{ + Name: "pairs", + Usage: "either a single currency pair string or comma delimiter string of pairs e.g. \"BTC-USD,XRP-USD\"", + }, + cli.StringFlag{ + Name: "asset", + Usage: "asset", + }, + }, + Action: enableDisableExchangePair, + }, + { + Name: "enable", + Usage: "enable pairs by asset type", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.StringFlag{ + Name: "pairs", + Usage: "either a single currency pair string or comma delimiter string of pairs e.g. \"BTC-USD,XRP-USD\"", + }, + cli.StringFlag{ + Name: "asset", + Usage: "asset", + }, + cli.BoolTFlag{ + Name: "enable", + Hidden: true, + }, + }, + Action: enableDisableExchangePair, + }, + { + Name: "enableall", + Usage: "enable all pairs", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.BoolTFlag{ + Name: "enable", + Hidden: true, + }, + }, + Action: enableDisableAllExchangePairs, + }, + { + Name: "disableall", + Usage: "dissable all pairs", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + }, + Action: enableDisableAllExchangePairs, + }, + { + Name: "update", + Usage: "fetches supported pairs from the exchange and updates available pairs and removes unsupported enable pairs", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + }, + Action: updateExchangeSupportedPairs, + }, + { + Name: "getassets", + Usage: "fetches supported assets", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + }, + Action: getExchangeAssets, + }, + }, +} + +func enableDisableExchangePair(c *cli.Context) error { + enable := c.BoolT("enable") + if c.NArg() == 0 && c.NumFlags() == 0 { + if enable { + return cli.ShowCommandHelp(c, "enable") + } + + return cli.ShowCommandHelp(c, "disable") + } + + var exchange string + var pairs string + var asset string + + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + exchange = c.Args().First() + } + + if !validExchange(exchange) { + return errInvalidExchange + } + + if c.IsSet("pairs") { + pairs = c.String("pairs") + } else { + pairs = c.Args().Get(1) + } + + if c.IsSet("asset") { + asset = c.String("asset") + } else { + asset = c.Args().Get(2) + } + + asset = strings.ToLower(asset) + if !validAsset(asset) { + return errInvalidAsset + } + + pairList := strings.Split(pairs, ",") + + var validPairs []*gctrpc.CurrencyPair + for i := range pairList { + if !validPair(pairList[i]) { + return errInvalidPair + } + + p, err := currency.NewPairFromString(pairList[i]) + if err != nil { + return err + } + + validPairs = append(validPairs, &gctrpc.CurrencyPair{ + Delimiter: p.Delimiter, + Base: p.Base.String(), + Quote: p.Quote.String(), + }) + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + client := gctrpc.NewGoCryptoTraderClient(conn) + + result, err := client.SetExchangePair(context.Background(), + &gctrpc.SetExchangePairRequest{ + Exchange: exchange, + Pairs: validPairs, + AssetType: asset, + Enable: enable, + }, + ) + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +func getExchangePairs(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowSubcommandHelp(c) + } + + var exchange string + var asset string + + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + exchange = c.Args().First() + } + + if !validExchange(exchange) { + return errInvalidExchange + } + + if c.IsSet("asset") { + asset = c.String("asset") + } else { + asset = c.Args().Get(1) + } + + asset = strings.ToLower(asset) + if !validAsset(asset) { + return errInvalidAsset + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.GetExchangePairs(context.Background(), + &gctrpc.GetExchangePairsRequest{ + Exchange: exchange, + Asset: asset, + }, + ) + if err != nil { + return err + } + jsonOutput(result) + return nil +} + +func enableDisableExchangeAsset(c *cli.Context) error { + enable := c.BoolT("enable") + if c.NArg() == 0 && c.NumFlags() == 0 { + if enable { + return cli.ShowCommandHelp(c, "enableasset") + } + return cli.ShowCommandHelp(c, "disableasset") + } + + var exchange string + var asset string + + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + exchange = c.Args().First() + } + + if !validExchange(exchange) { + return errInvalidExchange + } + + if c.IsSet("asset") { + asset = c.String("asset") + } else { + asset = c.Args().Get(1) + } + + asset = strings.ToLower(asset) + if !validAsset(asset) { + return errInvalidAsset + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.SetExchangeAsset(context.Background(), + &gctrpc.SetExchangeAssetRequest{ + Exchange: exchange, + Asset: asset, + Enable: enable, + }, + ) + if err != nil { + return err + } + jsonOutput(result) + return nil +} + +func enableDisableAllExchangePairs(c *cli.Context) error { + enable := c.BoolT("enable") + if c.NArg() == 0 && c.NumFlags() == 0 { + if enable { + return cli.ShowCommandHelp(c, "enableall") + } + return cli.ShowCommandHelp(c, "disableall") + } + + var exchange string + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + 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.SetAllExchangePairs(context.Background(), + &gctrpc.SetExchangeAllPairsRequest{ + Exchange: exchange, + Enable: enable, + }, + ) + if err != nil { + return err + } + jsonOutput(result) + return nil +} + +func updateExchangeSupportedPairs(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowSubcommandHelp(c) + } + + var exchange string + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + 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.UpdateExchangeSupportedPairs(context.Background(), + &gctrpc.UpdateExchangeSupportedPairsRequest{ + Exchange: exchange, + }, + ) + if err != nil { + return err + } + jsonOutput(result) + return nil +} + +func getExchangeAssets(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowSubcommandHelp(c) + } + + var exchange string + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + 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.GetExchangeAssets(context.Background(), + &gctrpc.GetExchangeAssetsRequest{ + Exchange: exchange, + }, + ) + if err != nil { + return err + } + jsonOutput(result) + return nil +} diff --git a/cmd/gctcli/websocket_management.go b/cmd/gctcli/websocket_management.go new file mode 100644 index 00000000..8b747e9c --- /dev/null +++ b/cmd/gctcli/websocket_management.go @@ -0,0 +1,270 @@ +package main + +import ( + "context" + "fmt" + + "github.com/thrasher-corp/gocryptotrader/gctrpc" + "github.com/urfave/cli" +) + +var websocketManagerCommand = cli.Command{ + Name: "websocket", + Usage: "execute websocket management command", + ArgsUsage: " ", + Subcommands: []cli.Command{ + { + Name: "getinfo", + Usage: "returns all exchange websocket information", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + }, + Action: getwebsocketInfo, + }, + { + Name: "disable", + Usage: "disables websocket connection for an exchange", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + }, + Action: enableDisableWebsocket, + }, + { + Name: "enable", + Usage: "enables websocket connection for an exchange", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.BoolTFlag{ + Name: "enable", + Hidden: true, + }, + }, + Action: enableDisableWebsocket, + }, + { + Name: "getsubs", + Usage: "returns current subscriptions for an exchange", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + }, + Action: getSubscriptions, + }, + { + Name: "setproxy", + Usage: "sets exchange websocket proxy, flushes and reroutes connection", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.StringFlag{ + Name: "proxy", + Usage: "proxy address to change to, if proxy string is not set, this will stop the utilization of the prior set proxy.", + }, + }, + Action: setProxy, + }, + { + Name: "seturl", + Usage: "sets exchange websocket endpoint URL and resets the websocket connection", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + cli.StringFlag{ + Name: "url", + Usage: "url string to change to, an empty string will set it back to the packaged defined default", + }, + }, + Action: setURL, + }, + }, +} + +func getwebsocketInfo(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowSubcommandHelp(c) + } + + var exchange string + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + exchange = c.Args().First() + } + + if !validExchange(exchange) { + return fmt.Errorf("[%s] is not a valid exchange", exchange) + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.WebsocketGetInfo(context.Background(), + &gctrpc.WebsocketGetInfoRequest{Exchange: exchange}) + if err != nil { + return err + } + jsonOutput(result) + return nil +} + +func enableDisableWebsocket(c *cli.Context) error { + enable := c.BoolT("enable") + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowSubcommandHelp(c) + } + + var exchange string + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + exchange = c.Args().First() + } + + if !validExchange(exchange) { + return fmt.Errorf("[%s] is not a valid exchange", exchange) + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.WebsocketSetEnabled(context.Background(), + &gctrpc.WebsocketSetEnabledRequest{Exchange: exchange, Enable: enable}) + if err != nil { + return err + } + jsonOutput(result) + return nil +} + +func getSubscriptions(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowSubcommandHelp(c) + } + + var exchange string + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + exchange = c.Args().First() + } + + if !validExchange(exchange) { + return fmt.Errorf("[%s] is not a valid exchange", exchange) + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.WebsocketGetSubscriptions(context.Background(), + &gctrpc.WebsocketGetSubscriptionsRequest{Exchange: exchange}) + if err != nil { + return err + } + jsonOutput(result) + return nil +} + +func setProxy(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowSubcommandHelp(c) + } + + var exchange string + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + exchange = c.Args().First() + } + + if !validExchange(exchange) { + return fmt.Errorf("[%s] is not a valid exchange", exchange) + } + + var proxy string + if c.IsSet("proxy") { + proxy = c.String("proxy") + } else { + proxy = c.Args().Get(1) + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.WebsocketSetProxy(context.Background(), + &gctrpc.WebsocketSetProxyRequest{Exchange: exchange, Proxy: proxy}) + if err != nil { + return err + } + jsonOutput(result) + return nil +} + +func setURL(c *cli.Context) error { + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowSubcommandHelp(c) + } + + var exchange string + if c.IsSet("exchange") { + exchange = c.String("exchange") + } else { + exchange = c.Args().First() + } + + if !validExchange(exchange) { + return fmt.Errorf("[%s] is not a valid exchange", exchange) + } + + var url string + if c.IsSet("url") { + url = c.String("url") + } else { + url = c.Args().Get(1) + } + + conn, err := setupClient() + if err != nil { + return err + } + defer conn.Close() + + client := gctrpc.NewGoCryptoTraderClient(conn) + result, err := client.WebsocketSetURL(context.Background(), + &gctrpc.WebsocketSetURLRequest{Exchange: exchange, Url: url}) + if err != nil { + return err + } + jsonOutput(result) + return nil +} diff --git a/common/common.go b/common/common.go index 67ee52a9..dd587ae0 100644 --- a/common/common.go +++ b/common/common.go @@ -361,3 +361,18 @@ func InArray(val, array interface{}) (exists bool, index int) { } return } + +// Errors defines multiple errors +type Errors []error + +// Error implements error interface +func (e Errors) Error() string { + if len(e) == 0 { + return "" + } + var r string + for i := range e { + r += e[i].Error() + ", " + } + return r[:len(r)-2] +} diff --git a/common/common_test.go b/common/common_test.go index 31006441..391ba2b0 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -1,6 +1,7 @@ package common import ( + "errors" "net/url" "os" "os/user" @@ -547,3 +548,18 @@ func TestInArray(t *testing.T) { t.Errorf("found a non existent value in the slice") } } + +func TestErrors(t *testing.T) { + var test Errors + if test.Error() != "" { + t.Fatal("string should be nil") + } + test = append(test, errors.New("test1")) + if test.Error() != "test1" { + t.Fatal("does not match error") + } + test = append(test, errors.New("test2")) + if test.Error() != "test1, test2" { + t.Fatal("does not match error") + } +} diff --git a/config/config.go b/config/config.go index 5467373e..4f3bb2af 100644 --- a/config/config.go +++ b/config/config.go @@ -345,71 +345,46 @@ func (c *Config) GetExchangeAssetTypes(exchName string) (asset.Items, error) { return nil, fmt.Errorf("exchange %s currency pairs is nil", exchName) } - return exchCfg.CurrencyPairs.AssetTypes, nil + return exchCfg.CurrencyPairs.GetAssetTypes(), nil } // SupportsExchangeAssetType returns whether or not the exchange supports the supplied asset type -func (c *Config) SupportsExchangeAssetType(exchName string, assetType asset.Item) (bool, error) { +func (c *Config) SupportsExchangeAssetType(exchName string, assetType asset.Item) error { exchCfg, err := c.GetExchangeConfig(exchName) if err != nil { - return false, err + return err } if exchCfg.CurrencyPairs == nil { - return false, fmt.Errorf("exchange %s currency pairs is nil", exchName) + return fmt.Errorf("exchange %s currency pairs is nil", exchName) } if !asset.IsValid(assetType) { - return false, fmt.Errorf("exchange %s invalid asset types", exchName) + return fmt.Errorf("exchange %s invalid asset type %s", + exchName, + assetType) } - return exchCfg.CurrencyPairs.AssetTypes.Contains(assetType), nil -} - -// CheckExchangeAssetsConsistency checks the exchanges supported assets compared to the stored -// entries and removes any non supported -func (c *Config) CheckExchangeAssetsConsistency(exchName string) { - exchCfg, err := c.GetExchangeConfig(exchName) - if err != nil { - return - } - - exchangeAssetTypes, err := c.GetExchangeAssetTypes(exchName) - if err != nil { - return - } - - 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]) - exchCfg.CurrencyPairs.Delete(storedAssetTypes[x]) - } + if !exchCfg.CurrencyPairs.GetAssetTypes().Contains(assetType) { + return fmt.Errorf("exchange %s unsupported asset type %s", + exchName, + assetType) } + return nil } // SetPairs sets the exchanges currency pairs func (c *Config) SetPairs(exchName string, assetType asset.Item, enabled bool, pairs currency.Pairs) error { - if len(pairs) == 0 { - return fmt.Errorf("pairs is nil") - } - exchCfg, err := c.GetExchangeConfig(exchName) if err != nil { return err } - supports, err := c.SupportsExchangeAssetType(exchName, assetType) + err = c.SupportsExchangeAssetType(exchName, assetType) if err != nil { return err } - if !supports { - return fmt.Errorf("exchange %s does not support asset type %v", exchName, assetType) - } - exchCfg.CurrencyPairs.StorePairs(assetType, pairs, enabled) return nil } @@ -421,16 +396,12 @@ func (c *Config) GetCurrencyPairConfig(exchName string, assetType asset.Item) (* return nil, err } - supports, err := c.SupportsExchangeAssetType(exchName, assetType) + err = c.SupportsExchangeAssetType(exchName, assetType) if err != nil { return nil, err } - if !supports { - return nil, fmt.Errorf("exchange %s does not support asset type %v", exchName, assetType) - } - - return exchCfg.CurrencyPairs.Get(assetType), nil + return exchCfg.CurrencyPairs.Get(assetType) } // CheckPairConfigFormats checks to see if the pair config format is valid @@ -508,60 +479,128 @@ func (c *Config) CheckPairConsistency(exchName string) error { return err } + var atLeastOneEnabled bool for x := range assetTypes { enabledPairs, err := c.GetEnabledPairs(exchName, assetTypes[x]) + if err == nil { + if len(enabledPairs) != 0 { + atLeastOneEnabled = true + continue + } + var enabled bool + enabled, err = c.AssetTypeEnabled(assetTypes[x], exchName) + if err != nil { + return err + } + + if !enabled { + continue + } + + var availPairs currency.Pairs + availPairs, err = c.GetAvailablePairs(exchName, assetTypes[x]) + if err != nil { + return err + } + + err = c.SetPairs(exchName, + assetTypes[x], + true, + currency.Pairs{availPairs.GetRandomPair()}) + if err != nil { + return err + } + atLeastOneEnabled = true + continue + } + + // On error an enabled pair is not found in the available pairs list + // so remove and report + availPairs, err := c.GetAvailablePairs(exchName, assetTypes[x]) if err != nil { return err } - availPairs, _ := c.GetAvailablePairs(exchName, assetTypes[x]) - if len(availPairs) == 0 { - continue - } - var pairs, pairsRemoved currency.Pairs - update := false - - if len(enabledPairs) > 0 { - for x := range enabledPairs { - if !availPairs.Contains(enabledPairs[x], true) { - update = true - pairsRemoved = append(pairsRemoved, enabledPairs[x]) - continue - } - pairs = append(pairs, enabledPairs[x]) + for x := range enabledPairs { + if !availPairs.Contains(enabledPairs[x], true) { + pairsRemoved = append(pairsRemoved, enabledPairs[x]) + continue } - } else { - update = true + pairs = append(pairs, enabledPairs[x]) } - if !update { + if len(pairsRemoved) == 0 { + return fmt.Errorf("check pair consistency fault for asset %s, conflict found but no pairs removed", + assetTypes[x]) + } + + // Flush corrupted/misspelled enabled pairs in config + err = c.SetPairs(exchName, assetTypes[x], true, pairs) + if err != nil { + return err + } + + log.Warnf(log.ConfigMgr, + "Exchange %s: [%v] Removing enabled pair(s) %v from enabled pairs list, as it isn't located in the available pairs list.\n", + exchName, + assetTypes[x], + pairsRemoved.Strings()) + + if len(pairs) != 0 { + atLeastOneEnabled = true continue } - if len(pairs) == 0 || len(enabledPairs) == 0 { - newPair := availPairs.GetRandomPair() - c.SetPairs(exchName, assetTypes[x], true, currency.Pairs{newPair}) - log.Warnf(log.ExchangeSys, "Exchange %s: [%v] No enabled pairs found in available pairs, randomly added %v pair.\n", - exchName, assetTypes[x], newPair) - continue - } else { - c.SetPairs(exchName, assetTypes[x], true, pairs) + enabled, err := c.AssetTypeEnabled(assetTypes[x], exchName) + if err != nil { + return err } - log.Warnf(log.ExchangeSys, "Exchange %s: [%v] Removing enabled pair(s) %v from enabled pairs as it isn't an available pair.\n", - exchName, assetTypes[x], pairsRemoved.Strings()) + + if !enabled { + continue + } + + err = c.SetPairs(exchName, + assetTypes[x], + true, + currency.Pairs{availPairs.GetRandomPair()}) + if err != nil { + return err + } + atLeastOneEnabled = true + } + + // If no pair is enabled across the entire range of assets, then atleast + // enable one and turn on the asset type + if !atLeastOneEnabled { + avail, err := c.GetAvailablePairs(exchName, assetTypes[0]) + if err != nil { + return err + } + + newPair := avail.GetRandomPair() + err = c.SetPairs(exchName, assetTypes[0], true, currency.Pairs{newPair}) + if err != nil { + return err + } + log.Warnf(log.ConfigMgr, + "Exchange %s: [%v] No enabled pairs found in available pairs list, randomly added %v pair.\n", + exchName, + assetTypes[0], + newPair) } return nil } // SupportsPair returns true or not whether the exchange supports the supplied // pair -func (c *Config) SupportsPair(exchName string, p currency.Pair, assetType asset.Item) (bool, error) { +func (c *Config) SupportsPair(exchName string, p currency.Pair, assetType asset.Item) bool { pairs, err := c.GetAvailablePairs(exchName, assetType) if err != nil { - return false, err + return false } - return pairs.Contains(p, false), nil + return pairs.Contains(p, false) } // GetPairFormat returns the exchanges pair config storage format @@ -571,25 +610,31 @@ func (c *Config) GetPairFormat(exchName string, assetType asset.Item) (currency. return currency.PairFormat{}, err } - supports, err := c.SupportsExchangeAssetType(exchName, assetType) + err = c.SupportsExchangeAssetType(exchName, assetType) if err != nil { return currency.PairFormat{}, err } - if !supports { - return currency.PairFormat{}, - fmt.Errorf("exchange %s does not support asset type %s", exchName, - assetType) - } - if exchCfg.CurrencyPairs.UseGlobalFormat { return *exchCfg.CurrencyPairs.ConfigFormat, nil } - p := exchCfg.CurrencyPairs.Get(assetType) + p, err := exchCfg.CurrencyPairs.Get(assetType) + if err != nil { + return currency.PairFormat{}, err + } + if p == nil { return currency.PairFormat{}, - fmt.Errorf("exchange %s pair store for asset type %s is nil", exchName, + fmt.Errorf("exchange %s pair store for asset type %s is nil", + exchName, + assetType) + } + + if p.ConfigFormat == nil { + return currency.PairFormat{}, + fmt.Errorf("exchange %s pair config format for asset type %s is nil", + exchName, assetType) } @@ -608,7 +653,11 @@ func (c *Config) GetAvailablePairs(exchName string, assetType asset.Item) (curre return nil, err } - pairs := exchCfg.CurrencyPairs.GetPairs(assetType, false) + pairs, err := exchCfg.CurrencyPairs.GetPairs(assetType, false) + if err != nil { + return nil, err + } + if pairs == nil { return nil, nil } @@ -618,7 +667,7 @@ func (c *Config) GetAvailablePairs(exchName string, assetType asset.Item) (curre } // GetEnabledPairs returns a list of currency pairs for a specifc exchange -func (c *Config) GetEnabledPairs(exchName string, assetType asset.Item) ([]currency.Pair, error) { +func (c *Config) GetEnabledPairs(exchName string, assetType asset.Item) (currency.Pairs, error) { exchCfg, err := c.GetExchangeConfig(exchName) if err != nil { return nil, err @@ -629,13 +678,19 @@ func (c *Config) GetEnabledPairs(exchName string, assetType asset.Item) ([]curre return nil, err } - pairs := exchCfg.CurrencyPairs.GetPairs(assetType, true) + pairs, err := exchCfg.CurrencyPairs.GetPairs(assetType, true) + if err != nil { + return pairs, err + } + if pairs == nil { return nil, nil } - return pairs.Format(pairFormat.Delimiter, pairFormat.Index, - pairFormat.Uppercase), nil + return pairs.Format(pairFormat.Delimiter, + pairFormat.Index, + pairFormat.Uppercase), + nil } // GetEnabledExchanges returns a list of enabled exchanges @@ -843,16 +898,6 @@ func (c *Config) CheckExchangeConfigValues() error { c.Exchanges[i].CurrencyPairs.ConfigFormat = c.Exchanges[i].ConfigCurrencyPairFormat c.Exchanges[i].CurrencyPairs.RequestFormat = c.Exchanges[i].RequestCurrencyPairFormat - if c.Exchanges[i].AssetTypes == nil { - c.Exchanges[i].CurrencyPairs.AssetTypes = asset.Items{ - asset.Spot, - } - } else { - c.Exchanges[i].CurrencyPairs.AssetTypes = asset.New( - strings.ToLower(*c.Exchanges[i].AssetTypes), - ) - } - var availPairs, enabledPairs currency.Pairs if c.Exchanges[i].AvailablePairs != nil { availPairs = *c.Exchanges[i].AvailablePairs @@ -865,8 +910,9 @@ func (c *Config) CheckExchangeConfigValues() error { c.Exchanges[i].CurrencyPairs.UseGlobalFormat = true c.Exchanges[i].CurrencyPairs.Store(asset.Spot, currency.PairStore{ - Available: availPairs, - Enabled: enabledPairs, + AssetEnabled: convert.BoolPtr(true), + Available: availPairs, + Enabled: enabledPairs, }, ) @@ -877,6 +923,50 @@ func (c *Config) CheckExchangeConfigValues() error { c.Exchanges[i].AssetTypes = nil c.Exchanges[i].AvailablePairs = nil c.Exchanges[i].EnabledPairs = nil + } else { + assets := c.Exchanges[i].CurrencyPairs.GetAssetTypes() + var atLeastOne bool + for index := range assets { + err := c.Exchanges[i].CurrencyPairs.IsAssetEnabled(assets[index]) + if err != nil { + // Checks if we have an old config without the ability to + // enable disable the entire asset + if err.Error() == "cannot ascertain if asset is enabled, variable is nil" { + log.Warnf(log.ConfigMgr, + "Exchange %s: upgrading config for asset type %s and setting enabled.\n", + c.Exchanges[i].Name, + assets[index]) + err = c.Exchanges[i].CurrencyPairs.SetAssetEnabled(assets[index], true) + if err != nil { + return err + } + atLeastOne = true + } + continue + } + atLeastOne = true + } + + if !atLeastOne { + if len(assets) == 0 { + c.Exchanges[i].Enabled = false + log.Warnf(log.ConfigMgr, + "%s no assets found, disabling...", + c.Exchanges[i].Name) + continue + } + + // turn on an asset if all disabled + log.Warnf(log.ConfigMgr, + "%s assets disabled, turning on asset %s", + c.Exchanges[i].Name, + assets[0]) + + err := c.Exchanges[i].CurrencyPairs.SetAssetEnabled(assets[0], true) + if err != nil { + return err + } + } } if c.Exchanges[i].Enabled { @@ -885,71 +975,101 @@ func (c *Config) CheckExchangeConfigValues() error { c.Exchanges[i].Enabled = false continue } - if (c.Exchanges[i].API.AuthenticatedSupport || c.Exchanges[i].API.AuthenticatedWebsocketSupport) && c.Exchanges[i].API.CredentialsValidator != nil { + if (c.Exchanges[i].API.AuthenticatedSupport || c.Exchanges[i].API.AuthenticatedWebsocketSupport) && + c.Exchanges[i].API.CredentialsValidator != nil { var failed bool - if c.Exchanges[i].API.CredentialsValidator.RequiresKey && (c.Exchanges[i].API.Credentials.Key == "" || c.Exchanges[i].API.Credentials.Key == DefaultAPIKey) { + if c.Exchanges[i].API.CredentialsValidator.RequiresKey && + (c.Exchanges[i].API.Credentials.Key == "" || c.Exchanges[i].API.Credentials.Key == DefaultAPIKey) { failed = true } - if c.Exchanges[i].API.CredentialsValidator.RequiresSecret && (c.Exchanges[i].API.Credentials.Secret == "" || c.Exchanges[i].API.Credentials.Secret == DefaultAPISecret) { + if c.Exchanges[i].API.CredentialsValidator.RequiresSecret && + (c.Exchanges[i].API.Credentials.Secret == "" || c.Exchanges[i].API.Credentials.Secret == DefaultAPISecret) { failed = true } - if c.Exchanges[i].API.CredentialsValidator.RequiresClientID && (c.Exchanges[i].API.Credentials.ClientID == DefaultAPIClientID || c.Exchanges[i].API.Credentials.ClientID == "") { + if c.Exchanges[i].API.CredentialsValidator.RequiresClientID && + (c.Exchanges[i].API.Credentials.ClientID == DefaultAPIClientID || c.Exchanges[i].API.Credentials.ClientID == "") { failed = true } if failed { c.Exchanges[i].API.AuthenticatedSupport = false c.Exchanges[i].API.AuthenticatedWebsocketSupport = false - log.Warnf(log.ExchangeSys, WarningExchangeAuthAPIDefaultOrEmptyValues, c.Exchanges[i].Name) + log.Warnf(log.ConfigMgr, WarningExchangeAuthAPIDefaultOrEmptyValues, c.Exchanges[i].Name) } } - if !c.Exchanges[i].Features.Supports.RESTCapabilities.AutoPairUpdates && !c.Exchanges[i].Features.Supports.WebsocketCapabilities.AutoPairUpdates { + if !c.Exchanges[i].Features.Supports.RESTCapabilities.AutoPairUpdates && + !c.Exchanges[i].Features.Supports.WebsocketCapabilities.AutoPairUpdates { lastUpdated := convert.UnixTimestampToTime(c.Exchanges[i].CurrencyPairs.LastUpdated) lastUpdated = lastUpdated.AddDate(0, 0, pairsLastUpdatedWarningThreshold) if lastUpdated.Unix() <= time.Now().Unix() { - log.Warnf(log.ExchangeSys, WarningPairsLastUpdatedThresholdExceeded, c.Exchanges[i].Name, pairsLastUpdatedWarningThreshold) + log.Warnf(log.ConfigMgr, + WarningPairsLastUpdatedThresholdExceeded, + c.Exchanges[i].Name, + pairsLastUpdatedWarningThreshold) } } if c.Exchanges[i].HTTPTimeout <= 0 { - log.Warnf(log.ExchangeSys, "Exchange %s HTTP Timeout value not set, defaulting to %v.\n", c.Exchanges[i].Name, defaultHTTPTimeout) + log.Warnf(log.ConfigMgr, + "Exchange %s HTTP Timeout value not set, defaulting to %v.\n", + c.Exchanges[i].Name, + defaultHTTPTimeout) c.Exchanges[i].HTTPTimeout = defaultHTTPTimeout } if c.Exchanges[i].WebsocketResponseCheckTimeout <= 0 { - log.Warnf(log.ExchangeSys, "Exchange %s Websocket response check timeout value not set, defaulting to %v.", - c.Exchanges[i].Name, defaultWebsocketResponseCheckTimeout) + log.Warnf(log.ConfigMgr, + "Exchange %s Websocket response check timeout value not set, defaulting to %v.", + c.Exchanges[i].Name, + defaultWebsocketResponseCheckTimeout) c.Exchanges[i].WebsocketResponseCheckTimeout = defaultWebsocketResponseCheckTimeout } if c.Exchanges[i].WebsocketResponseMaxLimit <= 0 { - log.Warnf(log.ExchangeSys, "Exchange %s Websocket response max limit value not set, defaulting to %v.", - c.Exchanges[i].Name, defaultWebsocketResponseMaxLimit) + log.Warnf(log.ConfigMgr, + "Exchange %s Websocket response max limit value not set, defaulting to %v.", + c.Exchanges[i].Name, + defaultWebsocketResponseMaxLimit) c.Exchanges[i].WebsocketResponseMaxLimit = defaultWebsocketResponseMaxLimit } if c.Exchanges[i].WebsocketTrafficTimeout <= 0 { - log.Warnf(log.ExchangeSys, "Exchange %s Websocket response traffic timeout value not set, defaulting to %v.", - c.Exchanges[i].Name, defaultWebsocketTrafficTimeout) + log.Warnf(log.ConfigMgr, + "Exchange %s Websocket response traffic timeout value not set, defaulting to %v.", + c.Exchanges[i].Name, + defaultWebsocketTrafficTimeout) c.Exchanges[i].WebsocketTrafficTimeout = defaultWebsocketTrafficTimeout } if c.Exchanges[i].WebsocketOrderbookBufferLimit <= 0 { - log.Warnf(log.ExchangeSys, "Exchange %s Websocket orderbook buffer limit value not set, defaulting to %v.", - c.Exchanges[i].Name, defaultWebsocketOrderbookBufferLimit) + log.Warnf(log.ConfigMgr, + "Exchange %s Websocket orderbook buffer limit value not set, defaulting to %v.", + c.Exchanges[i].Name, + defaultWebsocketOrderbookBufferLimit) c.Exchanges[i].WebsocketOrderbookBufferLimit = defaultWebsocketOrderbookBufferLimit } err := c.CheckPairConsistency(c.Exchanges[i].Name) if err != nil { - log.Errorf(log.ExchangeSys, "Exchange %s: CheckPairConsistency error: %s\n", c.Exchanges[i].Name, err) + log.Errorf(log.ConfigMgr, + "Exchange %s: CheckPairConsistency error: %s\n", + c.Exchanges[i].Name, + err) c.Exchanges[i].Enabled = false continue } - - c.CheckExchangeAssetsConsistency(c.Exchanges[i].Name) - + for x := range c.Exchanges[i].BankAccounts { + if !c.Exchanges[i].BankAccounts[x].Enabled { + continue + } + err := c.Exchanges[i].BankAccounts[x].Validate() + if err != nil { + c.Exchanges[i].BankAccounts[x].Enabled = false + log.Warnln(log.ConfigMgr, err.Error()) + } + } exchanges++ } } + if exchanges == 0 { return errors.New(ErrNoEnabledExchanges) } @@ -1104,8 +1224,8 @@ func (c *Config) RetrieveConfigCurrencyPairs(enabledOnly bool, assetType asset.I continue } - supports, _ := c.SupportsExchangeAssetType(c.Exchanges[x].Name, assetType) - if !supports { + err := c.SupportsExchangeAssetType(c.Exchanges[x].Name, assetType) + if err != nil { continue } @@ -1118,13 +1238,12 @@ func (c *Config) RetrieveConfigCurrencyPairs(enabledOnly bool, assetType asset.I } for x := range c.Exchanges { - supports, _ := c.SupportsExchangeAssetType(c.Exchanges[x].Name, assetType) - if !supports { + err := c.SupportsExchangeAssetType(c.Exchanges[x].Name, assetType) + if err != nil { continue } var pairs []currency.Pair - var err error if !c.Exchanges[x].Enabled && enabledOnly { pairs, err = c.GetEnabledPairs(c.Exchanges[x].Name, assetType) } else { @@ -1475,7 +1594,7 @@ func (c *Config) ReadConfig(configPath string, dryrun bool) error { } if !ConfirmECS(fileData) { - err = ConfirmConfigJSON(fileData, &c) + err = json.Unmarshal(fileData, c) if err != nil { return err } @@ -1493,38 +1612,39 @@ func (c *Config) ReadConfig(configPath string, dryrun bool) error { return c.SaveConfig(defaultPath, dryrun) } } - } else { - errCounter := 0 - for { - if errCounter >= maxAuthFailures { - return errors.New("failed to decrypt config after 3 attempts") - } - key, err := PromptForConfigKey(IsInitialSetup) - if err != nil { - log.Errorf(log.ConfigMgr, "PromptForConfigKey err: %s", err) - errCounter++ - continue - } + return nil + } - var f []byte - f = append(f, fileData...) - data, err := DecryptConfigFile(f, key) - if err != nil { - log.Errorf(log.ConfigMgr, "DecryptConfigFile err: %s", err) - errCounter++ - continue - } - - err = ConfirmConfigJSON(data, &c) - if err != nil { - if errCounter < maxAuthFailures { - log.Error(log.ConfigMgr, "Invalid password.") - } - errCounter++ - continue - } - break + errCounter := 0 + for { + if errCounter >= maxAuthFailures { + return errors.New("failed to decrypt config after 3 attempts") } + key, err := PromptForConfigKey(IsInitialSetup) + if err != nil { + log.Errorf(log.ConfigMgr, "PromptForConfigKey err: %s", err) + errCounter++ + continue + } + + var f []byte + f = append(f, fileData...) + data, err := DecryptConfigFile(f, key) + if err != nil { + log.Errorf(log.ConfigMgr, "DecryptConfigFile err: %s", err) + errCounter++ + continue + } + + err = json.Unmarshal(data, c) + if err != nil { + if errCounter < maxAuthFailures { + log.Error(log.ConfigMgr, "Invalid password.") + } + errCounter++ + continue + } + break } return nil } @@ -1611,12 +1731,16 @@ func (c *Config) CheckRemoteControlConfig() { func (c *Config) CheckConfig() error { err := c.CheckLoggerConfig() if err != nil { - log.Errorf(log.ConfigMgr, "Failed to configure logger, some logging features unavailable: %s\n", err) + log.Errorf(log.ConfigMgr, + "Failed to configure logger, some logging features unavailable: %s\n", + err) } err = c.checkDatabaseConfig() if err != nil { - log.Errorf(log.DatabaseMgr, "Failed to configure database: %v", err) + log.Errorf(log.DatabaseMgr, + "Failed to configure database: %v", + err) } err = c.CheckExchangeConfigValues() @@ -1626,7 +1750,9 @@ func (c *Config) CheckConfig() error { err = c.checkGCTScriptConfig() if err != nil { - log.Errorf(log.Global, "Failed to configure gctscript, feature has been disabled: %s\n", err) + log.Errorf(log.Global, + "Failed to configure gctscript, feature has been disabled: %s\n", + err) } c.CheckConnectionMonitorConfig() @@ -1641,7 +1767,9 @@ func (c *Config) CheckConfig() error { } if c.GlobalHTTPTimeout <= 0 { - log.Warnf(log.ConfigMgr, "Global HTTP Timeout value not set, defaulting to %v.\n", defaultHTTPTimeout) + log.Warnf(log.ConfigMgr, + "Global HTTP Timeout value not set, defaulting to %v.\n", + defaultHTTPTimeout) c.GlobalHTTPTimeout = defaultHTTPTimeout } @@ -1703,3 +1831,17 @@ func (c *Config) RemoveExchange(exchName string) bool { } return false } + +// AssetTypeEnabled checks to see if the asset type is enabled in configuration +func (c *Config) AssetTypeEnabled(a asset.Item, exch string) (bool, error) { + cfg, err := c.GetExchangeConfig(exch) + if err != nil { + return false, err + } + + err = cfg.CurrencyPairs.IsAssetEnabled(a) + if err != nil { + return false, nil + } + return true, nil +} diff --git a/config/config_encryption.go b/config/config_encryption.go index bf32e0e1..c2ba139a 100644 --- a/config/config_encryption.go +++ b/config/config_encryption.go @@ -5,7 +5,6 @@ import ( "crypto/aes" "crypto/cipher" "crypto/rand" - "encoding/json" "errors" "fmt" "io" @@ -167,11 +166,6 @@ func DecryptConfigFile(configData, key []byte) ([]byte, error) { return result, nil } -// ConfirmConfigJSON confirms JSON in file -func ConfirmConfigJSON(file []byte, result interface{}) error { - return json.Unmarshal(file, &result) -} - // ConfirmSalt checks whether the encrypted data contains a salt func ConfirmSalt(file []byte) bool { return bytes.Contains(file, []byte(SaltPrefix)) diff --git a/config/config_encryption_test.go b/config/config_encryption_test.go index 3eabe392..23ceef30 100644 --- a/config/config_encryption_test.go +++ b/config/config_encryption_test.go @@ -1,7 +1,6 @@ package config import ( - "io/ioutil" "testing" ) @@ -84,19 +83,6 @@ func TestDecryptConfigFile(t *testing.T) { } } -func TestConfirmConfigJSON(t *testing.T) { - var result interface{} - testConfirmJSON, err := ioutil.ReadFile(TestFile) - if err != nil { - t.Errorf("testConfirmJSON: %s", err) - } - - err = ConfirmConfigJSON(testConfirmJSON, &result) - if err != nil || result == nil { - t.Errorf("testConfirmJSON: %s", err) - } -} - func TestConfirmECS(t *testing.T) { t.Parallel() diff --git a/config/config_test.go b/config/config_test.go index 286bba71..82a814c4 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/connchecker" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/database" @@ -21,6 +22,7 @@ const ( defaultEnabledExchanges = 28 testFakeExchangeName = "Stampbit" testPair = "BTC-USD" + testString = "test" ) func TestGetCurrencyConfig(t *testing.T) { @@ -32,6 +34,22 @@ func TestGetCurrencyConfig(t *testing.T) { _ = cfg.GetCurrencyConfig() } +func TestGetClientBankAccounts(t *testing.T) { + cfg := GetConfig() + err := cfg.LoadConfig(TestFile, true) + if err != nil { + t.Fatal("GetExchangeBankAccounts LoadConfig error", err) + } + _, err = cfg.GetClientBankAccounts("Kraken", "USD") + if err != nil { + t.Error("GetExchangeBankAccounts error", err) + } + _, err = cfg.GetClientBankAccounts("noob exchange", "USD") + if err == nil { + t.Fatal("error cannot be nil") + } +} + func TestGetExchangeBankAccounts(t *testing.T) { cfg := GetConfig() err := cfg.LoadConfig(TestFile, true) @@ -48,6 +66,17 @@ func TestGetExchangeBankAccounts(t *testing.T) { } } +func TestCheckBankAccountConfig(t *testing.T) { + cfg := GetConfig() + err := cfg.LoadConfig(TestFile, true) + if err != nil { + t.Error("GetExchangeBankAccounts LoadConfig error", err) + } + + cfg.BankAccounts[0].Enabled = true + cfg.CheckBankAccountConfig() +} + func TestUpdateExchangeBankAccounts(t *testing.T) { cfg := GetConfig() err := cfg.LoadConfig(TestFile, true) @@ -84,7 +113,7 @@ func TestUpdateClientBankAccounts(t *testing.T) { if err != nil { t.Error("UpdateClientBankAccounts LoadConfig error", err) } - b := banking.Account{Enabled: false, BankName: "test", AccountNumber: "0234"} + b := banking.Account{Enabled: false, BankName: testString, AccountNumber: "0234"} err = cfg.UpdateClientBankAccounts(&b) if err != nil { t.Error("UpdateClientBankAccounts error", err) @@ -185,7 +214,7 @@ func TestPurgeExchangeCredentials(t *testing.T) { var c Config c.Exchanges = []ExchangeConfig{ { - Name: "test", + Name: testString, API: APIConfig{ AuthenticatedSupport: true, AuthenticatedWebsocketSupport: true, @@ -219,7 +248,7 @@ func TestPurgeExchangeCredentials(t *testing.T) { c.PurgeExchangeAPICredentials() - exchCfg, err := c.GetExchangeConfig("test") + exchCfg, err := c.GetExchangeConfig(testString) if err != nil { t.Error(err) } @@ -257,8 +286,8 @@ func TestUpdateCommunicationsConfig(t *testing.T) { if err != nil { t.Error("UpdateCommunicationsConfig LoadConfig error", err) } - cfg.UpdateCommunicationsConfig(&CommunicationsConfig{SlackConfig: SlackConfig{Name: "TEST"}}) - if cfg.Communications.SlackConfig.Name != "TEST" { + cfg.UpdateCommunicationsConfig(&CommunicationsConfig{SlackConfig: SlackConfig{Name: testString}}) + if cfg.Communications.SlackConfig.Name != testString { t.Error("UpdateCommunicationsConfig LoadConfig error") } } @@ -308,7 +337,7 @@ func TestCheckCommunicationsConfig(t *testing.T) { cfg.SMS = &SMSGlobalConfig{} cfg.Communications.SMSGlobalConfig.Name = "" cfg.CheckCommunicationsConfig() - if cfg.Communications.SMSGlobalConfig.Password != "test" { + if cfg.Communications.SMSGlobalConfig.Password != testString { t.Error("CheckCommunicationsConfig error:", err) } @@ -389,9 +418,9 @@ func TestGetExchangeAssetTypes(t *testing.T) { ExchangeConfig{ Name: testFakeExchangeName, CurrencyPairs: ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Futures, + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: new(currency.PairStore), + asset.Futures: new(currency.PairStore), }, }, }, @@ -403,7 +432,7 @@ func TestGetExchangeAssetTypes(t *testing.T) { t.Error(err) } - if assets.JoinToString(",") != "spot,futures" { + if !assets.Contains(asset.Spot) || !assets.Contains(asset.Futures) { t.Error("unexpected results") } @@ -417,7 +446,7 @@ func TestGetExchangeAssetTypes(t *testing.T) { func TestSupportsExchangeAssetType(t *testing.T) { t.Parallel() var c Config - _, err := c.SupportsExchangeAssetType("void", asset.Spot) + err := c.SupportsExchangeAssetType("void", asset.Spot) if err == nil { t.Error("Expected error for non-existent exchange") } @@ -426,73 +455,30 @@ func TestSupportsExchangeAssetType(t *testing.T) { ExchangeConfig{ Name: testFakeExchangeName, CurrencyPairs: ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Futures, + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: new(currency.PairStore), }, }, }, ) - supports, err := c.SupportsExchangeAssetType(testFakeExchangeName, asset.Spot) + 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") + err = c.SupportsExchangeAssetType(testFakeExchangeName, "asdf") if err == nil { t.Error("Expected error from invalid asset item") } c.Exchanges[0].CurrencyPairs = nil - _, err = c.SupportsExchangeAssetType(testFakeExchangeName, asset.Spot) + err = c.SupportsExchangeAssetType(testFakeExchangeName, asset.Spot) if err == nil { t.Error("Expected error from nil pair manager") } } -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 have been removed from the pair manager") - } -} - func TestSetPairs(t *testing.T) { t.Parallel() @@ -524,9 +510,8 @@ func TestSetPairs(t *testing.T) { } c.Exchanges[0].CurrencyPairs = ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Futures, + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: new(currency.PairStore), }, } @@ -562,10 +547,6 @@ func TestGetCurrencyPairConfig(t *testing.T) { } pm := ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Futures, - }, Pairs: map[asset.Item]*currency.PairStore{ asset.Spot: { RequestFormat: ¤cy.PairFormat{ @@ -610,11 +591,6 @@ func TestCheckPairConfigFormats(t *testing.T) { c.Exchanges = append(c.Exchanges, ExchangeConfig{ Name: testFakeExchangeName, - CurrencyPairs: ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Item("wrong"), - }, - }, }, ) @@ -622,23 +598,40 @@ func TestCheckPairConfigFormats(t *testing.T) { t.Error("nil pair store should return an error") } - c.Exchanges[0].CurrencyPairs.AssetTypes = asset.Items{asset.Spot} - c.Exchanges[0].CurrencyPairs.Pairs = map[asset.Item]*currency.PairStore{ - asset.Spot: { - RequestFormat: ¤cy.PairFormat{}, - ConfigFormat: ¤cy.PairFormat{}, + c.Exchanges[0].CurrencyPairs = ¤cy.PairsManager{ + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: {}, + asset.Futures: {}, }, - asset.Futures: { - RequestFormat: ¤cy.PairFormat{}, - ConfigFormat: ¤cy.PairFormat{}, + } + if err := c.CheckPairConfigFormats(testFakeExchangeName); err == nil { + t.Error("error cannot be nil") + } + + c.Exchanges[0].CurrencyPairs = ¤cy.PairsManager{ + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: { + RequestFormat: ¤cy.PairFormat{}, + ConfigFormat: ¤cy.PairFormat{}, + }, + asset.Futures: { + RequestFormat: ¤cy.PairFormat{}, + ConfigFormat: ¤cy.PairFormat{}, + }, }, } if err := c.CheckPairConfigFormats(testFakeExchangeName); err != nil { t.Error("nil pairs should be okay to continue") } - + avail, err := currency.NewPairDelimiter(testPair, "-") + if err != nil { + t.Fatal(err) + } + enabled, err := currency.NewPairDelimiter("BTC~USD", "~") + if err != nil { + t.Fatal(err) + } // Test having a pair index and delimiter set at the same time throws an error - c.Exchanges[0].CurrencyPairs.AssetTypes = asset.Items{asset.Spot} c.Exchanges[0].CurrencyPairs.Pairs = map[asset.Item]*currency.PairStore{ asset.Spot: { RequestFormat: ¤cy.PairFormat{ @@ -651,10 +644,10 @@ func TestCheckPairConfigFormats(t *testing.T) { Index: "USD", }, Available: currency.Pairs{ - currency.NewPairDelimiter(testPair, "-"), + avail, }, Enabled: currency.Pairs{ - currency.NewPairDelimiter("BTC~USD", "~"), + enabled, }, }, } @@ -698,11 +691,6 @@ func TestCheckPairConsistency(t *testing.T) { c.Exchanges = append(c.Exchanges, ExchangeConfig{ Name: testFakeExchangeName, - CurrencyPairs: ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - }, }, ) @@ -711,33 +699,71 @@ func TestCheckPairConsistency(t *testing.T) { t.Error("nil pair store should return an error") } - c.Exchanges[0].CurrencyPairs.Pairs = map[asset.Item]*currency.PairStore{ - asset.Spot: { - RequestFormat: ¤cy.PairFormat{ - Uppercase: false, - Delimiter: "_", - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "_", - }, - Enabled: currency.Pairs{ - currency.NewPairDelimiter("BTC_USD", "_"), + enabled, err := currency.NewPairDelimiter("BTC_USD", "_") + if err != nil { + t.Fatal(err) + } + + c.Exchanges[0].CurrencyPairs = ¤cy.PairsManager{ + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: { + RequestFormat: ¤cy.PairFormat{ + Uppercase: false, + Delimiter: "_", + }, + ConfigFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: "_", + }, + Enabled: currency.Pairs{ + enabled, + }, }, }, } // Test for nil avail pairs - if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { - t.Error("nil available pairs should continue") + err = c.CheckPairConsistency(testFakeExchangeName) + if err != nil { + t.Error(err) + } + + p1, err := currency.NewPairDelimiter("LTC_USD", "_") + if err != nil { + t.Fatal(err) } // Test that enabled pair is not found in the available pairs c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Available = currency.Pairs{ - currency.NewPairDelimiter("LTC_USD", "_"), + p1, } + + // LTC_USD is only found in the available pairs list and should therefor + // be added to the enabled pairs list due to the atLestOneEnabled code + err = c.CheckPairConsistency(testFakeExchangeName) + if err != nil { + t.Fatal(err) + } + + for _, item := range c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled { + if !item.Equal(p1) { + t.Fatal("LTC_USD should be contained in the enabled pairs list") + } + } + + p2, err := currency.NewPairDelimiter("BTC_USD", "_") + if err != nil { + t.Fatal(err) + } + + // Add the BTC_USD pair and see result + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Available = currency.Pairs{ + p1, + p2, + } + if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { - t.Error("unexpected result") + t.Fatal(err) } // Test that an empty enabled pair is populated with an available pair @@ -746,10 +772,14 @@ func TestCheckPairConsistency(t *testing.T) { t.Error("unexpected result") } + if len(c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled) != 1 { + t.Fatal("should be populated with atleast one currency pair") + } + // Test that an invalid enabled pair is removed from the list c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled = currency.Pairs{ - currency.NewPairDelimiter("LTC_USD", "_"), - currency.NewPairDelimiter("BTC_USD", "_"), + p1, + p2, } if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { t.Error("unexpected result") @@ -760,6 +790,48 @@ func TestCheckPairConsistency(t *testing.T) { if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { t.Error("unexpected result") } + + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].AssetEnabled = convert.BoolPtr(true) + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled = currency.Pairs{} + + // Test no conflict and atleast one on enabled asset type + if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { + t.Error("unexpected result") + } + + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].AssetEnabled = convert.BoolPtr(true) + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled = currency.Pairs{currency.NewPair(currency.DASH, currency.USD)} + + // Test with conflict and atleast one on enabled asset type + if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { + t.Error("unexpected result") + } + + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].AssetEnabled = convert.BoolPtr(false) + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled = currency.Pairs{} + + // Test no conflict and atleast one on disabled asset type + if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { + t.Error("unexpected result") + } + + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled = currency.Pairs{ + currency.NewPair(currency.DASH, currency.USD), + p1, + p2, + } + + // Test with conflict and atleast one on disabled asset type + if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { + t.Error("unexpected result") + } + + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].AssetEnabled = nil + + // assetType enabled failure check + if err := c.CheckPairConsistency(testFakeExchangeName); err != nil { + t.Error("unexpected result") + } } func TestSupportsPair(t *testing.T) { @@ -772,17 +844,15 @@ func TestSupportsPair(t *testing.T) { } assetType := asset.Spot - _, err = cfg.SupportsPair("asdf", - currency.NewPair(currency.BTC, currency.USD), assetType) - if err == nil { + if cfg.SupportsPair("asdf", + currency.NewPair(currency.BTC, currency.USD), assetType) { t.Error( "TestSupportsPair. Expected error from Non-existent exchange", ) } - _, err = cfg.SupportsPair("Bitfinex", - currency.NewPair(currency.BTC, currency.USD), assetType) - if err != nil { + if !cfg.SupportsPair("Bitfinex", + currency.NewPair(currency.BTC, currency.USD), assetType) { t.Errorf( "TestSupportsPair. Incorrect values. Err: %s", err, ) @@ -809,7 +879,26 @@ func TestGetPairFormat(t *testing.T) { } c.Exchanges[0].CurrencyPairs = ¤cy.PairsManager{ - AssetTypes: asset.Items{asset.Spot}, + UseGlobalFormat: false, + RequestFormat: ¤cy.PairFormat{ + Uppercase: false, + Delimiter: "_", + }, + ConfigFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: "_", + }, + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: nil, + }, + } + + _, err = c.GetPairFormat(testFakeExchangeName, asset.Spot) + if err == nil { + t.Error("Expected error from nil pair manager") + } + + c.Exchanges[0].CurrencyPairs = ¤cy.PairsManager{ UseGlobalFormat: true, RequestFormat: ¤cy.PairFormat{ Uppercase: false, @@ -819,6 +908,9 @@ func TestGetPairFormat(t *testing.T) { Uppercase: true, Delimiter: "_", }, + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: new(currency.PairStore), + }, } _, err = c.GetPairFormat(testFakeExchangeName, asset.Item("invalid")) if err == nil { @@ -876,12 +968,8 @@ func TestGetAvailablePairs(t *testing.T) { c.Exchanges = append(c.Exchanges, ExchangeConfig{ - Name: testFakeExchangeName, - CurrencyPairs: ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - }, + Name: testFakeExchangeName, + CurrencyPairs: ¤cy.PairsManager{}, }, ) @@ -923,12 +1011,8 @@ func TestGetEnabledPairs(t *testing.T) { c.Exchanges = append(c.Exchanges, ExchangeConfig{ - Name: testFakeExchangeName, - CurrencyPairs: ¤cy.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - }, + Name: testFakeExchangeName, + CurrencyPairs: ¤cy.PairsManager{}, }, ) @@ -953,6 +1037,11 @@ func TestGetEnabledPairs(t *testing.T) { c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled = currency.Pairs{ currency.NewPair(currency.BTC, currency.USD), } + + c.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Available = currency.Pairs{ + currency.NewPair(currency.BTC, currency.USD), + } + _, err = c.GetEnabledPairs(testFakeExchangeName, asset.Spot) if err != nil { t.Error(err) @@ -1183,16 +1272,15 @@ func TestCheckExchangeConfigValues(t *testing.T) { // Test API settings migration sptr := func(s string) *string { return &s } - bptr := func(b bool) *bool { return &b } int64ptr := func(i int64) *int64 { return &i } cfg.Exchanges[0].APIKey = sptr("awesomeKey") cfg.Exchanges[0].APISecret = sptr("meowSecret") cfg.Exchanges[0].ClientID = sptr("clientIDerino") cfg.Exchanges[0].APIAuthPEMKey = sptr("-----BEGIN EC PRIVATE KEY-----\nASDF\n-----END EC PRIVATE KEY-----\n") - cfg.Exchanges[0].APIAuthPEMKeySupport = bptr(true) - cfg.Exchanges[0].AuthenticatedAPISupport = bptr(true) - cfg.Exchanges[0].AuthenticatedWebsocketAPISupport = bptr(true) + cfg.Exchanges[0].APIAuthPEMKeySupport = convert.BoolPtr(true) + cfg.Exchanges[0].AuthenticatedAPISupport = convert.BoolPtr(true) + cfg.Exchanges[0].AuthenticatedWebsocketAPISupport = convert.BoolPtr(true) cfg.Exchanges[0].WebsocketURL = sptr("wss://1337") cfg.Exchanges[0].APIURL = sptr(APIURLNonDefaultMessage) cfg.Exchanges[0].APIURLSecondary = sptr(APIURLNonDefaultMessage) @@ -1230,8 +1318,8 @@ func TestCheckExchangeConfigValues(t *testing.T) { // Test feature and endpoint migrations migrations cfg.Exchanges[0].Features = nil - cfg.Exchanges[0].SupportsAutoPairUpdates = bptr(true) - cfg.Exchanges[0].Websocket = bptr(true) + cfg.Exchanges[0].SupportsAutoPairUpdates = convert.BoolPtr(true) + cfg.Exchanges[0].Websocket = convert.BoolPtr(true) cfg.Exchanges[0].API.Endpoints.URL = "" cfg.Exchanges[0].API.Endpoints.URLSecondary = "" cfg.Exchanges[0].API.Endpoints.WebsocketURL = "" @@ -1253,11 +1341,16 @@ func TestCheckExchangeConfigValues(t *testing.T) { t.Error("unexpected values") } + p1, err := currency.NewPairDelimiter(testPair, "-") + if err != nil { + t.Fatal(err) + } + // Test currency pair migration setupPairs := func(emptyAssets bool) { cfg.Exchanges[0].CurrencyPairs = nil p := currency.Pairs{ - currency.NewPairDelimiter(testPair, "-"), + p1, } cfg.Exchanges[0].PairsLastUpdated = int64ptr(1234567) @@ -1305,17 +1398,25 @@ func TestCheckExchangeConfigValues(t *testing.T) { t.Error("unexpected request format values") } - if cfg.Exchanges[0].CurrencyPairs.AssetTypes.JoinToString(",") != "spot" || + if !cfg.Exchanges[0].CurrencyPairs.GetAssetTypes().Contains(asset.Spot) || !cfg.Exchanges[0].CurrencyPairs.UseGlobalFormat { t.Error("unexpected results") } - pairs := cfg.Exchanges[0].CurrencyPairs.GetPairs(asset.Spot, true) + pairs, err := cfg.Exchanges[0].CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + t.Fatal(err) + } + if len(pairs) == 0 || pairs.Join() != testPair { t.Error("pairs not set properly") } - pairs = cfg.Exchanges[0].CurrencyPairs.GetPairs(asset.Spot, false) + pairs, err = cfg.Exchanges[0].CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + t.Fatal(err) + } + if len(pairs) == 0 || pairs.Join() != testPair { t.Error("pairs not set properly") } @@ -1334,24 +1435,10 @@ func TestCheckExchangeConfigValues(t *testing.T) { cfg.Exchanges[0].Features.Supports.RESTCapabilities.AutoPairUpdates = false cfg.Exchanges[0].Features.Supports.WebsocketCapabilities.AutoPairUpdates = false cfg.Exchanges[0].CurrencyPairs.LastUpdated = 0 - cfg.CheckExchangeConfigValues() - - // Test exchange pair consistency error - cfg.Exchanges[0].CurrencyPairs.UseGlobalFormat = false - backup := cfg.Exchanges[0].CurrencyPairs.Pairs[asset.Spot] - cfg.Exchanges[0].CurrencyPairs.Pairs[asset.Spot] = nil err = cfg.CheckExchangeConfigValues() if err != nil { t.Error(err) } - if cfg.Exchanges[0].Enabled { - t.Error("exchange should have been disabled") - } - - // Restore to previous state - cfg.Exchanges[0].Enabled = true - cfg.Exchanges[0].CurrencyPairs.UseGlobalFormat = true - cfg.Exchanges[0].CurrencyPairs.Pairs[asset.Spot] = backup // Test websocket and HTTP timeout values cfg.Exchanges[0].WebsocketResponseMaxLimit = 0 @@ -1419,6 +1506,10 @@ func TestCheckExchangeConfigValues(t *testing.T) { !cfg.Exchanges[0].API.AuthenticatedWebsocketSupport { t.Error("Expected AuthenticatedAPISupport and AuthenticatedWebsocketAPISupport to be false from invalid API keys") } + + // Make a sneaky copy for bank account testing + cpy := append(cfg.Exchanges[:0:0], cfg.Exchanges...) + // Test empty exchange name for an enabled exchange cfg.Exchanges[0].Enabled = true cfg.Exchanges[0].Name = "" @@ -1436,6 +1527,82 @@ func TestCheckExchangeConfigValues(t *testing.T) { if err == nil { t.Error("Expected error from no enabled exchanges") } + + cfg.Exchanges = cpy + // Check bank account validation for exchange + cfg.Exchanges[0].BankAccounts = []banking.Account{ + { + Enabled: true, + }, + } + + err = cfg.CheckExchangeConfigValues() + if err != nil { + t.Error(err) + } + + if cfg.Exchanges[0].BankAccounts[0].Enabled { + t.Fatal("bank aaccount details not provided this should disable") + } + + // Test international bank + cfg.Exchanges[0].BankAccounts[0].Enabled = true + cfg.Exchanges[0].BankAccounts[0].BankName = testString + cfg.Exchanges[0].BankAccounts[0].BankAddress = testString + cfg.Exchanges[0].BankAccounts[0].BankPostalCode = testString + cfg.Exchanges[0].BankAccounts[0].BankPostalCity = testString + cfg.Exchanges[0].BankAccounts[0].BankCountry = testString + cfg.Exchanges[0].BankAccounts[0].AccountName = testString + cfg.Exchanges[0].BankAccounts[0].SupportedCurrencies = "monopoly moneys" + cfg.Exchanges[0].BankAccounts[0].IBAN = "some iban" + cfg.Exchanges[0].BankAccounts[0].SWIFTCode = "some swifty" + + err = cfg.CheckExchangeConfigValues() + if err != nil { + t.Error(err) + } + + if !cfg.Exchanges[0].BankAccounts[0].Enabled { + t.Fatal("bank aaccount details provided this should not disable") + } + + // Test aussie bank + cfg.Exchanges[0].BankAccounts[0].Enabled = true + cfg.Exchanges[0].BankAccounts[0].BankName = testString + cfg.Exchanges[0].BankAccounts[0].BankAddress = testString + cfg.Exchanges[0].BankAccounts[0].BankPostalCode = testString + cfg.Exchanges[0].BankAccounts[0].BankPostalCity = testString + cfg.Exchanges[0].BankAccounts[0].BankCountry = testString + cfg.Exchanges[0].BankAccounts[0].AccountName = testString + cfg.Exchanges[0].BankAccounts[0].SupportedCurrencies = "AUD" + cfg.Exchanges[0].BankAccounts[0].BSBNumber = "some BSB nonsense" + cfg.Exchanges[0].BankAccounts[0].IBAN = "" + cfg.Exchanges[0].BankAccounts[0].SWIFTCode = "" + + err = cfg.CheckExchangeConfigValues() + if err != nil { + t.Error(err) + } + + if !cfg.Exchanges[0].BankAccounts[0].Enabled { + t.Fatal("bank account details provided this should not disable") + } + + cfg.Exchanges = nil + cfg.Exchanges = append(cfg.Exchanges, cpy[0]) + + cfg.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].Enabled = nil + cfg.Exchanges[0].CurrencyPairs.Pairs[asset.Spot].AssetEnabled = convert.BoolPtr(false) + err = cfg.CheckExchangeConfigValues() + if err != nil { + t.Error(err) + } + + cfg.Exchanges[0].CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + err = cfg.CheckExchangeConfigValues() + if err == nil { + t.Error("err cannot be nil") + } } func TestRetrieveConfigCurrencyPairs(t *testing.T) { diff --git a/config_example.json b/config_example.json index 1b5216a2..1bd16444 100644 --- a/config_example.json +++ b/config_example.json @@ -449,7 +449,7 @@ "websocketCapabilities": {} }, "enabled": { - "autoPairUpdates": false, + "autoPairUpdates": true, "websocketAPI": false } }, diff --git a/currency/code.go b/currency/code.go index 634f2100..18aa61f8 100644 --- a/currency/code.go +++ b/currency/code.go @@ -6,8 +6,6 @@ import ( "fmt" "strings" "unicode" - - "github.com/thrasher-corp/gocryptotrader/common" ) func (r Role) String() string { @@ -68,24 +66,24 @@ func (b *BaseCodes) HasData() bool { // GetFullCurrencyData returns a type that is read to dump to file func (b *BaseCodes) GetFullCurrencyData() (File, error) { var file File - for _, i := range b.Items { - switch i.Role { + for i := range b.Items { + switch b.Items[i].Role { case Unset: - file.UnsetCurrency = append(file.UnsetCurrency, *i) + file.UnsetCurrency = append(file.UnsetCurrency, *b.Items[i]) case Fiat: - file.FiatCurrency = append(file.FiatCurrency, *i) + file.FiatCurrency = append(file.FiatCurrency, *b.Items[i]) case Cryptocurrency: - file.Cryptocurrency = append(file.Cryptocurrency, *i) + file.Cryptocurrency = append(file.Cryptocurrency, *b.Items[i]) case Token: - file.Token = append(file.Token, *i) + file.Token = append(file.Token, *b.Items[i]) case Contract: - file.Contracts = append(file.Contracts, *i) + file.Contracts = append(file.Contracts, *b.Items[i]) default: return file, errors.New("role undefined") } } - file.LastMainUpdate = b.LastMainUpdate + file.LastMainUpdate = b.LastMainUpdate.Unix() return file, nil } @@ -103,50 +101,11 @@ func (b *BaseCodes) GetCurrencies() Currencies { return currencies } -// UpdateCryptocurrency updates or registers a cryptocurrency -func (b *BaseCodes) UpdateCryptocurrency(fullName, symbol string, id int) error { - b.mtx.Lock() - defer b.mtx.Unlock() - for i := range b.Items { - if b.Items[i].Symbol != symbol { - continue - } - if b.Items[i].Role != Unset { - if b.Items[i].Role != Cryptocurrency { - if b.Items[i].FullName != "" { - if b.Items[i].FullName != fullName { - // multiple symbols found, break this and add the - // full context - this most likely won't occur for - // fiat but could occur for contracts. - break - } - } - return fmt.Errorf("role already defined in cryptocurrency %s as [%s]", - b.Items[i].Symbol, - b.Items[i].Role) - } - b.Items[i].FullName = fullName - b.Items[i].ID = id - return nil - } - - b.Items[i].Role = Cryptocurrency - b.Items[i].FullName = fullName - b.Items[i].ID = id - return nil +// UpdateCurrency updates or registers a currency/contract +func (b *BaseCodes) UpdateCurrency(fullName, symbol, blockchain string, id int, r Role) error { + if r == Unset { + return fmt.Errorf("role cannot be unset in update currency for %s", symbol) } - - b.Items = append(b.Items, &Item{ - FullName: fullName, - Symbol: symbol, - ID: id, - Role: Cryptocurrency, - }) - return nil -} - -// UpdateFiatCurrency updates or registers a fiat currency -func (b *BaseCodes) UpdateFiatCurrency(fullName, symbol string, id int) error { b.mtx.Lock() defer b.mtx.Unlock() for i := range b.Items { @@ -154,115 +113,31 @@ func (b *BaseCodes) UpdateFiatCurrency(fullName, symbol string, id int) error { continue } - if b.Items[i].Role != Unset { - if b.Items[i].Role != Fiat { - return fmt.Errorf("role already defined in fiat currency %s as [%s]", - b.Items[i].Symbol, - b.Items[i].Role) - } + if b.Items[i].Role == Unset { b.Items[i].FullName = fullName + b.Items[i].Role = r + b.Items[i].AssocChain = blockchain b.Items[i].ID = id return nil } - b.Items[i].Role = Fiat + if b.Items[i].Role != r { + // Captures same name currencies and duplicates to different roles + break + } + b.Items[i].FullName = fullName + b.Items[i].AssocChain = blockchain b.Items[i].ID = id return nil } b.Items = append(b.Items, &Item{ - FullName: fullName, - Symbol: symbol, - ID: id, - Role: Fiat, - }) - return nil -} - -// UpdateToken updates or registers a token -func (b *BaseCodes) UpdateToken(fullName, symbol, assocBlockchain string, id int) error { - b.mtx.Lock() - defer b.mtx.Unlock() - for i := range b.Items { - if b.Items[i].Symbol != symbol { - continue - } - - if b.Items[i].Role != Unset { - if b.Items[i].Role != Token { - if b.Items[i].FullName != "" { - if b.Items[i].FullName != fullName { - // multiple symbols found, break this and add the - // full context - this most likely won't occur for - // fiat but could occur for contracts. - break - } - } - return fmt.Errorf("role already defined in token %s as [%s]", - b.Items[i].Symbol, - b.Items[i].Role) - } - b.Items[i].FullName = fullName - b.Items[i].ID = id - b.Items[i].AssocChain = assocBlockchain - return nil - } - - b.Items[i].Role = Token - b.Items[i].FullName = fullName - b.Items[i].ID = id - b.Items[i].AssocChain = assocBlockchain - return nil - } - - b.Items = append(b.Items, &Item{ - FullName: fullName, Symbol: symbol, + FullName: fullName, + Role: r, + AssocChain: blockchain, ID: id, - Role: Token, - AssocChain: assocBlockchain, - }) - return nil -} - -// UpdateContract updates or registers a contract -func (b *BaseCodes) UpdateContract(fullName, symbol, assocExchange string) error { - b.mtx.Lock() - defer b.mtx.Unlock() - for i := range b.Items { - if b.Items[i].Symbol != symbol { - continue - } - - if b.Items[i].Role != Unset { - if b.Items[i].Role != Contract { - return fmt.Errorf("role already defined in contract %s as [%s]", - b.Items[i].Symbol, - b.Items[i].Role) - } - b.Items[i].FullName = fullName - if !common.StringDataContains(b.Items[i].AssocExchange, assocExchange) { - b.Items[i].AssocExchange = append(b.Items[i].AssocExchange, - assocExchange) - } - return nil - } - - b.Items[i].Role = Contract - b.Items[i].FullName = fullName - if !common.StringDataContains(b.Items[i].AssocExchange, assocExchange) { - b.Items[i].AssocExchange = append(b.Items[i].AssocExchange, - assocExchange) - } - return nil - } - - b.Items = append(b.Items, &Item{ - FullName: fullName, - Symbol: symbol, - Role: Contract, - AssocExchange: []string{assocExchange}, }) return nil } @@ -293,42 +168,38 @@ func (b *BaseCodes) Register(c string) Code { } } - newItem := Item{Symbol: NewUpperCode} - newCode := Code{ - Item: &newItem, + newItem := &Item{Symbol: NewUpperCode} + b.Items = append(b.Items, newItem) + + return Code{ + Item: newItem, UpperCase: format, } - - b.Items = append(b.Items, newCode.Item) - return newCode } // RegisterFiat registers a fiat currency from a string and returns a currency // code -func (b *BaseCodes) RegisterFiat(c string) (Code, error) { +func (b *BaseCodes) RegisterFiat(c string) Code { c = strings.ToUpper(c) b.mtx.Lock() defer b.mtx.Unlock() for i := range b.Items { if b.Items[i].Symbol == c { - if b.Items[i].Role != Unset { - if b.Items[i].Role != Fiat { - return Code{}, fmt.Errorf("register fiat error role already defined in fiat %s as [%s]", - b.Items[i].Symbol, - b.Items[i].Role) - } - return Code{Item: b.Items[i], UpperCase: true}, nil + if b.Items[i].Role == Unset { + b.Items[i].Role = Fiat } - b.Items[i].Role = Fiat - return Code{Item: b.Items[i], UpperCase: true}, nil + + if b.Items[i].Role != Fiat { + continue + } + return Code{Item: b.Items[i], UpperCase: true} } } item := &Item{Symbol: c, Role: Fiat} b.Items = append(b.Items, item) - - return Code{Item: item, UpperCase: true}, nil + return Code{Item: item, UpperCase: true} } // LoadItem sets item data @@ -336,41 +207,18 @@ func (b *BaseCodes) LoadItem(item *Item) error { b.mtx.Lock() defer b.mtx.Unlock() for i := range b.Items { - if b.Items[i].Symbol == item.Symbol { - if b.Items[i].Role == Unset { - b.Items[i].AssocChain = item.AssocChain - b.Items[i].AssocExchange = item.AssocExchange - b.Items[i].ID = item.ID - b.Items[i].Role = item.Role - b.Items[i].FullName = item.FullName - return nil - } - - if b.Items[i].FullName != "" { - if b.Items[i].FullName == item.FullName { - b.Items[i].AssocChain = item.AssocChain - b.Items[i].AssocExchange = item.AssocExchange - b.Items[i].ID = item.ID - b.Items[i].Role = item.Role - return nil - } - break - } - - if b.Items[i].ID == item.ID { - b.Items[i].AssocChain = item.AssocChain - b.Items[i].AssocExchange = item.AssocExchange - b.Items[i].FullName = item.FullName - b.Items[i].ID = item.ID - b.Items[i].Role = item.Role - return nil - } - - return fmt.Errorf("currency %s not found in currencycode list", - item.Symbol) + if b.Items[i].Symbol != item.Symbol || + (b.Items[i].Role != Unset && + item.Role != Unset && + b.Items[i].Role != item.Role) { + continue } + b.Items[i].AssocChain = item.AssocChain + b.Items[i].ID = item.ID + b.Items[i].Role = item.Role + b.Items[i].FullName = item.FullName + return nil } - b.Items = append(b.Items, item) return nil } diff --git a/currency/code_test.go b/currency/code_test.go index 1568a7e4..7ebb0aca 100644 --- a/currency/code_test.go +++ b/currency/code_test.go @@ -176,41 +176,51 @@ func TestBaseCode(t *testing.T) { false) } - err := main.UpdateContract("Bitcoin Perpetual", "XBTUSD", "Bitmex") + main.Register("XBTUSD") + + err := main.UpdateCurrency("Bitcoin Perpetual", + "XBTUSD", + "", + 0, + Contract) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) } - err = main.UpdateCryptocurrency("Bitcoin", "BTC", 1337) + main.Register("BTC") + err = main.UpdateCurrency("Bitcoin", "BTC", "", 1337, Cryptocurrency) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) } - err = main.UpdateFiatCurrency("Unreal Dollar", "AUD", 1111) + main.Register("AUD") + err = main.UpdateCurrency("Unreal Dollar", "AUD", "", 1111, Fiat) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) } + if main.Items[5].FullName != "Unreal Dollar" { t.Error("Expected fullname to update for AUD") } - err = main.UpdateFiatCurrency("Australian Dollar", "AUD", 1336) + err = main.UpdateCurrency("Australian Dollar", "AUD", "", 1336, Fiat) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) } main.Items[5].Role = Unset - err = main.UpdateFiatCurrency("Australian Dollar", "AUD", 1336) + err = main.UpdateCurrency("Australian Dollar", "AUD", "", 1336, Fiat) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) } if main.Items[5].Role != Fiat { t.Error("Expected role to change to Fiat") } - err = main.UpdateToken("Populous", "PPT", "ETH", 1335) + main.Register("PPT") + err = main.UpdateCurrency("Populous", "PPT", "ETH", 1335, Token) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) } contract := main.Register("XBTUSD") @@ -235,15 +245,12 @@ func TestBaseCode(t *testing.T) { true) } - err = main.LoadItem(&Item{ + main.LoadItem(&Item{ ID: 0, FullName: "Cardano", Role: Cryptocurrency, Symbol: "ADA", }) - if err != nil { - t.Error("BaseCode LoadItem() error", err) - } full, err := main.GetFullCurrencyData() if err != nil { @@ -275,8 +282,8 @@ func TestBaseCode(t *testing.T) { len(full.UnsetCurrency)) } - if !full.LastMainUpdate.IsZero() { - t.Errorf("BaseCode GetFullCurrencyData() error expected 0 but received %s", + if full.LastMainUpdate.(int64) != -62135596800 { + t.Errorf("BaseCode GetFullCurrencyData() error expected -62135596800 but received %d", full.LastMainUpdate) } @@ -295,41 +302,35 @@ func TestBaseCode(t *testing.T) { } main.Items[0].FullName = "Hello" - err = main.UpdateCryptocurrency("MEWOW", "CATS", 1338) + err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Cryptocurrency) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) } if main.Items[0].FullName != "MEWOW" { t.Error("Fullname not updated") } - err = main.UpdateCryptocurrency("MEWOW", "CATS", 1338) + err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Cryptocurrency) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) + } + err = main.UpdateCurrency("WOWCATS", "CATS", "", 3, Token) + if err != nil { + t.Fatal(err) } - main.Items[0].Role = Cryptocurrency - err = main.UpdateCryptocurrency("MEWOW", "CATS", 3) - if err != nil { - t.Error("BaseCode UpdateContract error", err) - } - if main.Items[0].ID != 3 { + // Creates a new item under a different currency role + if main.Items[9].ID != 3 { t.Error("ID not updated") } main.Items[0].Role = Unset - err = main.UpdateCryptocurrency("MEWOW", "CATS", 1338) + err = main.UpdateCurrency("MEWOW", "CATS", "", 1338, Cryptocurrency) if err != nil { - t.Error("BaseCode UpdateContract error", err) + t.Fatal(err) } if main.Items[0].ID != 1338 { t.Error("ID not updated") } - - main.Items[0].Role = Token - err = main.UpdateCryptocurrency("MEWOW", "CATS", 3) - if err == nil { - t.Error("Expecting cryptocurrency to already exist") - } } func TestCodeString(t *testing.T) { diff --git a/currency/code_types.go b/currency/code_types.go index c1b934ef..47800d4c 100644 --- a/currency/code_types.go +++ b/currency/code_types.go @@ -41,12 +41,11 @@ type Code struct { // Item defines a sub type containing the main attributes of a designated // currency code pointer type Item struct { - ID int `json:"id"` - FullName string `json:"fullName"` - Symbol string `json:"symbol"` - Role Role `json:"role"` - AssocChain string `json:"associatedBlockchain"` - AssocExchange []string `json:"associatedExchanges"` + ID int `json:"id,omitempty"` + FullName string `json:"fullName,omitempty"` + Symbol string `json:"symbol"` + Role Role `json:"-"` + AssocChain string `json:"associatedBlockchain,omitempty"` } // Const declarations for individual currencies/tokens/fiat diff --git a/currency/coinmarketcap/coinmarketcap.go b/currency/coinmarketcap/coinmarketcap.go index d664c181..e158ed28 100644 --- a/currency/coinmarketcap/coinmarketcap.go +++ b/currency/coinmarketcap/coinmarketcap.go @@ -8,7 +8,6 @@ package coinmarketcap import ( "context" "errors" - "fmt" "net/http" "net/url" "strconv" @@ -17,6 +16,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/log" ) // SetDefaults sets default values for the exchange @@ -62,8 +62,8 @@ func (c *Coinmarketcap) GetCryptocurrencyInfo(currencyID ...int64) (CryptoCurren } var currStr []string - for _, d := range currencyID { - currStr = append(currStr, strconv.FormatInt(d, 10)) + for i := range currencyID { + currStr = append(currStr, strconv.FormatInt(currencyID[i], 10)) } val := url.Values{} @@ -667,7 +667,6 @@ func (c *Coinmarketcap) GetPriceConversion(amount float64, currencyID int64, atH func (c *Coinmarketcap) SendHTTPRequest(method, endpoint string, v url.Values, result interface{}) error { headers := make(map[string]string) headers["Accept"] = "application/json" - headers["Accept-Encoding"] = "deflate, gzip" headers["X-CMC_PRO_API_KEY"] = c.APIkey path := c.APIUrl + c.APIVersion + endpoint @@ -710,7 +709,8 @@ func (c *Coinmarketcap) SetAccountPlan(s string) error { case "enterprise": c.Plan = Enterprise default: - return fmt.Errorf("account plan %s not found", s) + log.Warnf(log.Global, "account plan %s not found, defaulting to basic", s) + c.Plan = Basic } return nil } diff --git a/currency/coinmarketcap/coinmarketcap_test.go b/currency/coinmarketcap/coinmarketcap_test.go index 4d878d73..93d54c5a 100644 --- a/currency/coinmarketcap/coinmarketcap_test.go +++ b/currency/coinmarketcap/coinmarketcap_test.go @@ -44,11 +44,6 @@ func TestSetup(t *testing.T) { if err := c.Setup(cfg); err != nil { t.Error(err) } - - cfg.AccountPlan = "meow" - if err := c.Setup(cfg); err == nil { - t.Error("expected err when invalid account plan is specified") - } } func TestCheckAccountPlan(t *testing.T) { @@ -405,8 +400,4 @@ func TestSetAccountPlan(t *testing.T) { } } } - - if err := c.SetAccountPlan("bra"); err == nil { - t.Error("SetAccountPlan() error cannot be nil") - } } diff --git a/currency/conversion.go b/currency/conversion.go index 363366f7..edeec928 100644 --- a/currency/conversion.go +++ b/currency/conversion.go @@ -110,22 +110,14 @@ func (c *ConversionRates) Update(m map[string]float64) error { var list []Code // Verification list, cross check all currencies coming in var mainBaseCurrency Code - for key, val := range m { - code1, err := storage.ValidateFiatCode(key[:3]) - if err != nil { - return err - } + code1 := storage.ValidateFiatCode(key[:3]) if mainBaseCurrency == (Code{}) { mainBaseCurrency = code1 } - code2, err := storage.ValidateFiatCode(key[3:]) - if err != nil { - return err - } - + code2 := storage.ValidateFiatCode(key[3:]) if code1 == code2 { // Get rid of same conversions continue } diff --git a/currency/currencies.go b/currency/currencies.go index 2647fbc1..caa6cb97 100644 --- a/currency/currencies.go +++ b/currency/currencies.go @@ -23,8 +23,8 @@ type Currencies []Code // Strings returns an array of currency strings func (c Currencies) Strings() []string { var list []string - for _, d := range c { - list = append(list, d.String()) + for i := range c { + list = append(list, c[i].String()) } return list } @@ -53,8 +53,9 @@ func (c *Currencies) UnmarshalJSON(d []byte) error { } var allTheCurrencies Currencies - for _, data := range strings.Split(configCurrencies, ",") { - allTheCurrencies = append(allTheCurrencies, NewCode(data)) + curr := strings.Split(configCurrencies, ",") + for i := range curr { + allTheCurrencies = append(allTheCurrencies, NewCode(curr[i])) } *c = allTheCurrencies @@ -72,17 +73,14 @@ func (c Currencies) Match(other Currencies) bool { return false } - for _, d := range c { - var found bool - for i := range other { - if d == other[i] { - found = true - break +match: + for x := range c { + for y := range other { + if c[x] == other[y] { + continue match } } - if !found { - return false - } + return false } return true } diff --git a/currency/currencies_test.go b/currency/currencies_test.go index d97df832..f6d950c9 100644 --- a/currency/currencies_test.go +++ b/currency/currencies_test.go @@ -47,3 +47,18 @@ func TestCurrenciesMarshalJSON(t *testing.T) { expected, string(encoded)) } } + +func TestMatch(t *testing.T) { + matchString := []string{"btc", "usd", "ltc", "bro", "things"} + c := NewCurrenciesFromStringArray(matchString) + + if !c.Match(NewCurrenciesFromStringArray(matchString)) { + t.Fatal("should match") + } + if c.Match(NewCurrenciesFromStringArray([]string{"btc", "usd", "ltc", "bro"})) { + t.Fatal("should not match") + } + if c.Match(NewCurrenciesFromStringArray([]string{"btc", "usd", "ltc", "bro", "garbo"})) { + t.Fatal("should not match") + } +} diff --git a/currency/currency.go b/currency/currency.go index 475f4642..3415bf42 100644 --- a/currency/currency.go +++ b/currency/currency.go @@ -71,9 +71,14 @@ func GetTotalMarketCryptocurrencies() ([]Code, error) { return storage.GetTotalMarketCryptocurrencies() } -// RunStorageUpdater runs a new foreign exchange updater instance -func RunStorageUpdater(o BotOverrides, m *MainConfiguration, filepath string, v bool) error { - return storage.RunUpdater(o, m, filepath, v) +// RunStorageUpdater runs a new foreign exchange updater instance +func RunStorageUpdater(o BotOverrides, m *MainConfiguration, filepath string) error { + return storage.RunUpdater(o, m, filepath) +} + +// ShutdownStorageUpdater cleanly shuts down and saves to currency.json +func ShutdownStorageUpdater() error { + return storage.Shutdown() } // CopyPairFormat copies the pair format from a list of pairs once matched @@ -100,17 +105,23 @@ func FormatPairs(pairs []string, delimiter, index string) (Pairs, error) { continue } var p Pair + var err error if delimiter != "" { - p = NewPairDelimiter(pairs[x], delimiter) + p, err = NewPairDelimiter(pairs[x], delimiter) + if err != nil { + return nil, err + } } else { if index != "" { - var err error p, err = NewPairFromIndex(pairs[x], index) if err != nil { return Pairs{}, err } } else { - p = NewPairFromStrings(pairs[x][0:3], pairs[x][3:]) + p, err = NewPairFromStrings(pairs[x][0:3], pairs[x][3:]) + if err != nil { + return Pairs{}, err + } } } result = append(result, p) diff --git a/currency/currency_types.go b/currency/currency_types.go index 641a6e1a..83b5b077 100644 --- a/currency/currency_types.go +++ b/currency/currency_types.go @@ -53,10 +53,24 @@ type FXSettings struct { // File defines a full currency file generated by the currency storage // analysis system type File struct { - LastMainUpdate time.Time `json:"lastMainUpdate"` - Cryptocurrency []Item `json:"cryptocurrencies"` - FiatCurrency []Item `json:"fiatCurrencies"` - UnsetCurrency []Item `json:"unsetCurrencies"` - Contracts []Item `json:"contracts"` - Token []Item `json:"tokens"` + LastMainUpdate interface{} `json:"lastMainUpdate"` + Cryptocurrency []Item `json:"cryptocurrencies"` + FiatCurrency []Item `json:"fiatCurrencies"` + UnsetCurrency []Item `json:"unsetCurrencies"` + Contracts []Item `json:"contracts"` + Token []Item `json:"tokens"` } + +// Const here are packaged defined delimiters +const ( + UnderscoreDelimiter = "_" + DashDelimiter = "-" + ForwardSlashDelimiter = "/" + ColonDelimiter = ":" +) + +// delimiters is a delimiter list +var delimiters = []string{UnderscoreDelimiter, + DashDelimiter, + ForwardSlashDelimiter, + ColonDelimiter} diff --git a/currency/manager.go b/currency/manager.go index c77aaa36..9485d93c 100644 --- a/currency/manager.go +++ b/currency/manager.go @@ -2,14 +2,16 @@ package currency import ( "errors" + "fmt" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" ) // GetAssetTypes returns a list of stored asset types func (p *PairsManager) GetAssetTypes() asset.Items { - p.m.Lock() - defer p.m.Unlock() + p.m.RLock() + defer p.m.RUnlock() var assetTypes asset.Items for k := range p.Pairs { assetTypes = append(assetTypes, k) @@ -18,28 +20,23 @@ func (p *PairsManager) GetAssetTypes() asset.Items { } // Get gets the currency pair config based on the asset type -func (p *PairsManager) Get(a asset.Item) *PairStore { - p.m.Lock() - defer p.m.Unlock() +func (p *PairsManager) Get(a asset.Item) (*PairStore, error) { + p.m.RLock() + defer p.m.RUnlock() c, ok := p.Pairs[a] if !ok { - return nil + return nil, + fmt.Errorf("cannot get pair store, asset type %s not supported", a) } - return c + return c, nil } // Store stores a new currency pair config based on its asset type func (p *PairsManager) Store(a asset.Item, ps PairStore) { p.m.Lock() - if p.Pairs == nil { p.Pairs = make(map[asset.Item]*PairStore) } - - if !p.AssetTypes.Contains(a) { - p.AssetTypes = append(p.AssetTypes, a) - } - p.Pairs[a] = &ps p.m.Unlock() } @@ -51,37 +48,35 @@ func (p *PairsManager) Delete(a asset.Item) { if p.Pairs == nil { return } - - _, ok := p.Pairs[a] - if !ok { - return - } - delete(p.Pairs, a) } // GetPairs gets a list of stored pairs based on the asset type and whether // they're enabled or not -func (p *PairsManager) GetPairs(a asset.Item, enabled bool) Pairs { - p.m.Lock() - defer p.m.Unlock() +func (p *PairsManager) GetPairs(a asset.Item, enabled bool) (Pairs, error) { + p.m.RLock() + defer p.m.RUnlock() if p.Pairs == nil { - return nil + return nil, nil } c, ok := p.Pairs[a] if !ok { - return nil + return nil, nil } - var pairs Pairs if enabled { - pairs = c.Enabled - } else { - pairs = c.Available + for i := range c.Enabled { + if !c.Available.Contains(c.Enabled[i], true) { + return c.Enabled, + fmt.Errorf("enabled pair %s of asset type %s not contained in available list", + c.Enabled[i], + a) + } + } + return c.Enabled, nil } - - return pairs + return c.Available, nil } // StorePairs stores a list of pairs based on the asset type and whether @@ -96,7 +91,8 @@ func (p *PairsManager) StorePairs(a asset.Item, pairs Pairs, enabled bool) { c, ok := p.Pairs[a] if !ok { - c = new(PairStore) + p.Pairs[a] = new(PairStore) + c = p.Pairs[a] } if enabled { @@ -104,8 +100,6 @@ func (p *PairsManager) StorePairs(a asset.Item, pairs Pairs, enabled bool) { } else { c.Available = pairs } - - p.Pairs[a] = c } // DisablePair removes the pair from the enabled pairs list if found @@ -113,17 +107,9 @@ func (p *PairsManager) DisablePair(a asset.Item, pair Pair) error { p.m.Lock() defer p.m.Unlock() - if p.Pairs == nil { - return errors.New("pair manager not initialised") - } - - c, ok := p.Pairs[a] - if !ok { - return errors.New("asset type not found") - } - - if c == nil { - return errors.New("currency store is nil") + c, err := p.getPairStore(a) + if err != nil { + return err } if !c.Enabled.Contains(pair, true) { @@ -140,27 +126,82 @@ func (p *PairsManager) EnablePair(a asset.Item, pair Pair) error { p.m.Lock() defer p.m.Unlock() - if p.Pairs == nil { - return errors.New("pair manager not initialised") - } - - c, ok := p.Pairs[a] - if !ok { - return errors.New("asset type not found") - } - - if c == nil { - return errors.New("currency store is nil") + c, err := p.getPairStore(a) + if err != nil { + return err } if !c.Available.Contains(pair, true) { - return errors.New("specified pair was not found in the list of available pairs") + return fmt.Errorf("%s pair was not found in the list of available pairs", + pair) } if c.Enabled.Contains(pair, true) { - return errors.New("specified pair is already enabled") + return fmt.Errorf("%s pair is already enabled", pair) } c.Enabled = c.Enabled.Add(pair) return nil } + +// IsAssetEnabled checks to see if an asset is enabled +func (p *PairsManager) IsAssetEnabled(a asset.Item) error { + p.m.RLock() + defer p.m.RUnlock() + + c, err := p.getPairStore(a) + if err != nil { + return err + } + + if c.AssetEnabled == nil { + return errors.New("cannot ascertain if asset is enabled, variable is nil") + } + + if !*c.AssetEnabled { + return errors.New("asset not enabled") + } + return nil +} + +// SetAssetEnabled sets if an asset is enabled or disabled for first run +func (p *PairsManager) SetAssetEnabled(a asset.Item, enabled bool) error { + p.m.Lock() + defer p.m.Unlock() + + c, err := p.getPairStore(a) + if err != nil { + return err + } + + if c.AssetEnabled == nil { + c.AssetEnabled = convert.BoolPtr(enabled) + return nil + } + + if !*c.AssetEnabled && !enabled { + return errors.New("asset already disabled") + } else if *c.AssetEnabled && enabled { + return errors.New("asset already enabled") + } + + *c.AssetEnabled = enabled + return nil +} + +func (p *PairsManager) getPairStore(a asset.Item) (*PairStore, error) { + if p.Pairs == nil { + return nil, errors.New("pair manager not initialised") + } + + c, ok := p.Pairs[a] + if !ok { + return nil, errors.New("asset type not found") + } + + if c == nil { + return nil, errors.New("currency store is nil") + } + + return c, nil +} diff --git a/currency/manager_test.go b/currency/manager_test.go index 2d2f0bbd..554c4f00 100644 --- a/currency/manager_test.go +++ b/currency/manager_test.go @@ -8,11 +8,21 @@ import ( var p PairsManager -func initTest() { +func initTest(t *testing.T) { + spotAvailable, err := NewPairsFromStrings([]string{"BTC-USD", "LTC-USD"}) + if err != nil { + t.Fatal(err) + } + + spotEnabled, err := NewPairsFromStrings([]string{"BTC-USD"}) + if err != nil { + t.Fatal(err) + } + p.Store(asset.Spot, PairStore{ - Available: NewPairsFromStrings([]string{"BTC-USD", "LTC-USD"}), - Enabled: NewPairsFromStrings([]string{"BTC-USD"}), + Available: spotAvailable, + Enabled: spotEnabled, RequestFormat: &PairFormat{ Uppercase: true, }, @@ -25,7 +35,7 @@ func initTest() { } func TestGetAssetTypes(t *testing.T) { - initTest() + initTest(t) a := p.GetAssetTypes() if len(a) == 0 { @@ -38,22 +48,34 @@ func TestGetAssetTypes(t *testing.T) { } func TestGet(t *testing.T) { - initTest() + initTest(t) - if p.Get(asset.Spot) == nil { - t.Error("Spot assets shouldn't be nil") + _, err := p.Get(asset.Spot) + if err != nil { + t.Error(err) } - if p.Get(asset.Futures) != nil { + _, err = p.Get(asset.Futures) + if err == nil { t.Error("Futures should be nil") } } func TestStore(t *testing.T) { + availPairs, err := NewPairsFromStrings([]string{"BTC-USD", "LTC-USD"}) + if err != nil { + t.Fatal(err) + } + + enabledPairs, err := NewPairsFromStrings([]string{"BTC-USD"}) + if err != nil { + t.Fatal(err) + } + p.Store(asset.Futures, PairStore{ - Available: NewPairsFromStrings([]string{"BTC-USD", "LTC-USD"}), - Enabled: NewPairsFromStrings([]string{"BTC-USD"}), + Available: availPairs, + Enabled: enabledPairs, RequestFormat: &PairFormat{ Uppercase: true, }, @@ -64,7 +86,12 @@ func TestStore(t *testing.T) { }, ) - if p.Get(asset.Futures) == nil { + f, err := p.Get(asset.Futures) + if err != nil { + t.Fatal(err) + } + + if f == nil { t.Error("Futures assets shouldn't be nil") } } @@ -73,67 +100,131 @@ func TestDelete(t *testing.T) { p.Pairs = nil p.Delete(asset.Spot) - p.Store(asset.Spot, - PairStore{ - Available: NewPairsFromStrings([]string{"BTC-USD"}), - }, - ) + btcusdPairs, err := NewPairsFromStrings([]string{"BTC-USD"}) + if err != nil { + t.Fatal(err) + } + + p.Store(asset.Spot, PairStore{ + Available: btcusdPairs, + }) + p.Delete(asset.UpsideProfitContract) - if p.Get(asset.Spot) == nil { + spotPS, err := p.Get(asset.Spot) + if err != nil { + t.Fatal(err) + } + + if spotPS == nil { t.Error("AssetTypeSpot should exist") } p.Delete(asset.Spot) - if p.Get(asset.Spot) != nil { + + if _, err := p.Get(asset.Spot); err == nil { t.Error("Delete should have deleted AssetTypeSpot") } } func TestGetPairs(t *testing.T) { p.Pairs = nil - pairs := p.GetPairs(asset.Spot, true) + pairs, err := p.GetPairs(asset.Spot, true) + if err != nil { + t.Fatal(err) + } + if pairs != nil { t.Fatal("pairs shouldn't be populated") } - initTest() - pairs = p.GetPairs(asset.Spot, true) + initTest(t) + pairs, err = p.GetPairs(asset.Spot, true) + if err != nil { + t.Fatal(err) + } if pairs == nil { t.Fatal("pairs should be populated") } - pairs = p.GetPairs("blah", true) + pairs, err = p.GetPairs("blah", true) + if err != nil { + t.Fatal(err) + } + if pairs != nil { t.Fatal("pairs shouldn't be populated") } + + superfluous := NewPair(DASH, USDT) + newPairs := p.Pairs[asset.Spot].Enabled.Add(superfluous) + p.Pairs[asset.Spot].Enabled = newPairs + + _, err = p.GetPairs(asset.Spot, true) + if err == nil { + t.Fatal("error cannot be nil") + } } func TestStorePairs(t *testing.T) { p.Pairs = nil - p.StorePairs(asset.Spot, NewPairsFromStrings([]string{"ETH-USD"}), false) - pairs := p.GetPairs(asset.Spot, false) - if !pairs.Contains(NewPairFromString("ETH-USD"), true) { + + ethusdPairs, err := NewPairsFromStrings([]string{"ETH-USD"}) + if err != nil { + t.Fatal(err) + } + + p.StorePairs(asset.Spot, ethusdPairs, false) + pairs, err := p.GetPairs(asset.Spot, false) + if err != nil { + t.Fatal(err) + } + + ethusd, err := NewPairFromString("ETH-USD") + if err != nil { + t.Fatal(err) + } + + if !pairs.Contains(ethusd, true) { t.Errorf("TestStorePairs failed, unexpected result") } - initTest() - p.StorePairs(asset.Spot, NewPairsFromStrings([]string{"ETH-USD"}), false) - pairs = p.GetPairs(asset.Spot, false) + initTest(t) + p.StorePairs(asset.Spot, ethusdPairs, false) + pairs, err = p.GetPairs(asset.Spot, false) + if err != nil { + t.Fatal(err) + } + if pairs == nil { t.Errorf("pairs should be populated") } - if !pairs.Contains(NewPairFromString("ETH-USD"), true) { + if !pairs.Contains(ethusd, true) { t.Errorf("TestStorePairs failed, unexpected result") } - p.StorePairs(asset.Futures, NewPairsFromStrings([]string{"ETH-KRW"}), true) - pairs = p.GetPairs(asset.Futures, true) + ethkrwPairs, err := NewPairsFromStrings([]string{"ETH-KRW"}) + if err != nil { + t.Error(err) + } + + p.StorePairs(asset.Futures, ethkrwPairs, true) + p.StorePairs(asset.Futures, ethkrwPairs, false) + pairs, err = p.GetPairs(asset.Futures, true) + if err != nil { + t.Fatal(err) + } + if pairs == nil { t.Errorf("pairs futures should be populated") } - if !pairs.Contains(NewPairFromString("ETH-KRW"), true) { + ethkrw, err := NewPairFromString("ETH-KRW") + if err != nil { + t.Error(err) + } + + if !pairs.Contains(ethkrw, true) { t.Errorf("TestStorePairs failed, unexpected result") } } @@ -146,7 +237,7 @@ func TestDisablePair(t *testing.T) { } // Test asset type which doesn't exist - initTest() + initTest(t) if err := p.DisablePair(asset.Futures, Pair{}); err == nil { t.Error("unexpected result") } @@ -158,7 +249,7 @@ func TestDisablePair(t *testing.T) { } // Test disabling a pair which isn't enabled - initTest() + initTest(t) if err := p.DisablePair(asset.Spot, NewPair(LTC, USD)); err == nil { t.Error("unexpected result") } @@ -177,7 +268,7 @@ func TestEnablePair(t *testing.T) { } // Test asset type which doesn't exist - initTest() + initTest(t) if err := p.EnablePair(asset.Futures, Pair{}); err == nil { t.Error("unexpected result") } @@ -189,7 +280,7 @@ func TestEnablePair(t *testing.T) { } // Test enabling a pair which isn't in the list of available pairs - initTest() + initTest(t) if err := p.EnablePair(asset.Spot, NewPair(ETH, USD)); err == nil { t.Error("unexpected result") } @@ -204,3 +295,55 @@ func TestEnablePair(t *testing.T) { t.Error("unexpected result") } } + +func TestIsAssetEnabled_SetAssetEnabled(t *testing.T) { + p.Pairs = nil + // Test enabling a pair when the pair manager is not initialised + err := p.IsAssetEnabled(asset.Spot) + if err == nil { + t.Error("unexpected result") + } + + err = p.SetAssetEnabled(asset.Spot, true) + if err == nil { + t.Fatal("unexpected result") + } + + // Test asset type which doesn't exist + initTest(t) + + err = p.IsAssetEnabled(asset.Spot) + if err == nil { + t.Error("unexpected result") + } + + err = p.SetAssetEnabled(asset.Spot, false) + if err != nil { + t.Fatal(err) + } + + err = p.SetAssetEnabled(asset.Spot, false) + if err == nil { + t.Fatal("unexpected result") + } + + err = p.IsAssetEnabled(asset.Spot) + if err == nil { + t.Error("unexpected result") + } + + err = p.SetAssetEnabled(asset.Spot, true) + if err != nil { + t.Fatal(err) + } + + err = p.SetAssetEnabled(asset.Spot, true) + if err == nil { + t.Fatal("unexpected result") + } + + err = p.IsAssetEnabled(asset.Spot) + if err != nil { + t.Error("unexpected result") + } +} diff --git a/currency/manager_types.go b/currency/manager_types.go index 456cea10..ad2540ca 100644 --- a/currency/manager_types.go +++ b/currency/manager_types.go @@ -12,13 +12,13 @@ type PairsManager struct { ConfigFormat *PairFormat `json:"configFormat,omitempty"` UseGlobalFormat bool `json:"useGlobalFormat,omitempty"` LastUpdated int64 `json:"lastUpdated,omitempty"` - AssetTypes asset.Items `json:"assetTypes"` Pairs map[asset.Item]*PairStore `json:"pairs"` - m sync.Mutex + m sync.RWMutex } // PairStore stores a currency pair store type PairStore struct { + AssetEnabled *bool `json:"assetEnabled"` Enabled Pairs `json:"enabled"` Available Pairs `json:"available"` RequestFormat *PairFormat `json:"requestFormat,omitempty"` diff --git a/currency/pair.go b/currency/pair.go index bf4ff95b..ccc9e985 100644 --- a/currency/pair.go +++ b/currency/pair.go @@ -1,15 +1,24 @@ package currency import ( - "encoding/json" "fmt" "strings" ) // NewPairDelimiter splits the desired currency string at delimeter, the returns // a Pair struct -func NewPairDelimiter(currencyPair, delimiter string) Pair { +func NewPairDelimiter(currencyPair, delimiter string) (Pair, error) { + if !strings.Contains(currencyPair, delimiter) { + return Pair{}, + fmt.Errorf("delimiter: [%s] not found in currencypair string", delimiter) + } result := strings.Split(currencyPair, delimiter) + if len(result) < 2 { + return Pair{}, + fmt.Errorf("supplied pair: [%s] cannot be split with %s", + currencyPair, + delimiter) + } if len(result) > 2 { result[1] = strings.Join(result[1:], delimiter) } @@ -17,15 +26,24 @@ func NewPairDelimiter(currencyPair, delimiter string) Pair { Delimiter: delimiter, Base: NewCode(result[0]), Quote: NewCode(result[1]), - } + }, nil } // NewPairFromStrings returns a CurrencyPair without a delimiter -func NewPairFromStrings(baseCurrency, quoteCurrency string) Pair { - return Pair{ - Base: NewCode(baseCurrency), - Quote: NewCode(quoteCurrency), +func NewPairFromStrings(base, quote string) (Pair, error) { + if strings.Contains(base, " ") { + return Pair{}, + fmt.Errorf("cannot create pair, invalid base currency string [%s]", + base) } + + if strings.Contains(quote, " ") { + return Pair{}, + fmt.Errorf("cannot create pair, invalid quote currency string [%s]", + quote) + } + + return Pair{Base: NewCode(base), Quote: NewCode(quote)}, nil } // NewPair returns a currency pair from currency codes @@ -55,23 +73,24 @@ func NewPairFromIndex(currencyPair, index string) (Pair, error) { } if i == 0 { return NewPairFromStrings(currencyPair[0:len(index)], - currencyPair[len(index):]), - nil + currencyPair[len(index):]) } - return NewPairFromStrings(currencyPair[0:i], currencyPair[i:]), nil + return NewPairFromStrings(currencyPair[0:i], currencyPair[i:]) } // NewPairFromString converts currency string into a new CurrencyPair // with or without delimeter -func NewPairFromString(currencyPair string) Pair { - delimiters := []string{"_", "-", "/", ":"} - var delimiter string - for _, x := range delimiters { - if strings.Contains(currencyPair, x) { - delimiter = x - return NewPairDelimiter(currencyPair, delimiter) +func NewPairFromString(currencyPair string) (Pair, error) { + for x := range delimiters { + if strings.Contains(currencyPair, delimiters[x]) { + return NewPairDelimiter(currencyPair, delimiters[x]) } } + if len(currencyPair) < 3 { + return Pair{}, + fmt.Errorf("cannot create pair from %s string", + currencyPair) + } return NewPairFromStrings(currencyPair[0:3], currencyPair[3:]) } @@ -79,129 +98,12 @@ func NewPairFromString(currencyPair string) Pair { // with a specific format. This is helpful for exchanges which // provide currency pairs with no delimiter so we can match it with a list and // apply the same format -func NewPairFromFormattedPairs(currencyPair string, pairs Pairs, pairFmt PairFormat) Pair { +func NewPairFromFormattedPairs(currencyPair string, pairs Pairs, pairFmt PairFormat) (Pair, error) { for x := range pairs { - if strings.EqualFold(pairs[x].Format(pairFmt.Delimiter, - pairFmt.Uppercase).String(), currencyPair) { - return pairs[x] + fPair := pairs[x].Format(pairFmt.Delimiter, pairFmt.Uppercase) + if strings.EqualFold(fPair.String(), currencyPair) { + return pairs[x], nil } } return NewPairFromString(currencyPair) } - -// String returns a currency pair string -func (p Pair) String() string { - return p.Base.String() + p.Delimiter + p.Quote.String() -} - -// Lower converts the pair object to lowercase -func (p Pair) Lower() Pair { - return Pair{ - Delimiter: p.Delimiter, - Base: p.Base.Lower(), - Quote: p.Quote.Lower(), - } -} - -// Upper converts the pair object to uppercase -func (p Pair) Upper() Pair { - return Pair{ - Delimiter: p.Delimiter, - Base: p.Base.Upper(), - Quote: p.Quote.Upper(), - } -} - -// UnmarshalJSON comforms type to the umarshaler interface -func (p *Pair) UnmarshalJSON(d []byte) error { - var pair string - err := json.Unmarshal(d, &pair) - if err != nil { - return err - } - - *p = NewPairFromString(pair) - return nil -} - -// MarshalJSON conforms type to the marshaler interface -func (p Pair) MarshalJSON() ([]byte, error) { - return json.Marshal(p.String()) -} - -// Format changes the currency based on user preferences overriding the default -// String() display -func (p Pair) Format(delimiter string, uppercase bool) Pair { - p.Delimiter = delimiter - - if uppercase { - return p.Upper() - } - return p.Lower() -} - -// Equal compares two currency pairs and returns whether or not they are equal -func (p Pair) Equal(cPair Pair) bool { - return strings.EqualFold(p.Base.String(), cPair.Base.String()) && - strings.EqualFold(p.Quote.String(), cPair.Quote.String()) -} - -// EqualIncludeReciprocal compares two currency pairs and returns whether or not -// they are the same including reciprocal currencies. -func (p Pair) EqualIncludeReciprocal(cPair Pair) bool { - if p.Base.Item == cPair.Base.Item && - p.Quote.Item == cPair.Quote.Item || - p.Base.Item == cPair.Quote.Item && - p.Quote.Item == cPair.Base.Item { - return true - } - return false -} - -// IsCryptoPair checks to see if the pair is a crypto pair e.g. BTCLTC -func (p Pair) IsCryptoPair() bool { - return storage.IsCryptocurrency(p.Base) && - storage.IsCryptocurrency(p.Quote) -} - -// IsCryptoFiatPair checks to see if the pair is a crypto fiat pair e.g. BTCUSD -func (p Pair) IsCryptoFiatPair() bool { - return storage.IsCryptocurrency(p.Base) && - storage.IsFiatCurrency(p.Quote) || - storage.IsFiatCurrency(p.Base) && - storage.IsCryptocurrency(p.Quote) -} - -// IsFiatPair checks to see if the pair is a fiat pair e.g. EURUSD -func (p Pair) IsFiatPair() bool { - return storage.IsFiatCurrency(p.Base) && storage.IsFiatCurrency(p.Quote) -} - -// IsInvalid checks invalid pair if base and quote are the same -func (p Pair) IsInvalid() bool { - return p.Base.Item == p.Quote.Item -} - -// Swap turns the currency pair into its reciprocal -func (p Pair) Swap() Pair { - p.Base, p.Quote = p.Quote, p.Base - return p -} - -// IsEmpty returns whether or not the pair is empty or is missing a currency -// code -func (p Pair) IsEmpty() bool { - return p.Base.IsEmpty() || p.Quote.IsEmpty() -} - -// ContainsCurrency checks to see if a pair contains a specific currency -func (p Pair) ContainsCurrency(c Code) bool { - return p.Base.Item == c.Item || p.Quote.Item == c.Item -} - -// Pair holds currency pair information -type Pair struct { - Delimiter string `json:"delimiter"` - Base Code `json:"base"` - Quote Code `json:"quote"` -} diff --git a/currency/pair_methods.go b/currency/pair_methods.go new file mode 100644 index 00000000..f377d07c --- /dev/null +++ b/currency/pair_methods.go @@ -0,0 +1,117 @@ +package currency + +import ( + "encoding/json" + "strings" +) + +// String returns a currency pair string +func (p Pair) String() string { + return p.Base.String() + p.Delimiter + p.Quote.String() +} + +// Lower converts the pair object to lowercase +func (p Pair) Lower() Pair { + return Pair{ + Delimiter: p.Delimiter, + Base: p.Base.Lower(), + Quote: p.Quote.Lower(), + } +} + +// Upper converts the pair object to uppercase +func (p Pair) Upper() Pair { + return Pair{ + Delimiter: p.Delimiter, + Base: p.Base.Upper(), + Quote: p.Quote.Upper(), + } +} + +// UnmarshalJSON comforms type to the umarshaler interface +func (p *Pair) UnmarshalJSON(d []byte) error { + var pair string + err := json.Unmarshal(d, &pair) + if err != nil { + return err + } + + newPair, err := NewPairFromString(pair) + if err != nil { + return err + } + + p.Base = newPair.Base + p.Quote = newPair.Quote + p.Delimiter = newPair.Delimiter + return nil +} + +// MarshalJSON conforms type to the marshaler interface +func (p Pair) MarshalJSON() ([]byte, error) { + return json.Marshal(p.String()) +} + +// Format changes the currency based on user preferences overriding the default +// String() display +func (p Pair) Format(delimiter string, uppercase bool) Pair { + newP := Pair{Base: p.Base, Quote: p.Quote, Delimiter: delimiter} + if uppercase { + return newP.Upper() + } + return newP.Lower() +} + +// Equal compares two currency pairs and returns whether or not they are equal +func (p Pair) Equal(cPair Pair) bool { + return strings.EqualFold(p.Base.String(), cPair.Base.String()) && + strings.EqualFold(p.Quote.String(), cPair.Quote.String()) +} + +// EqualIncludeReciprocal compares two currency pairs and returns whether or not +// they are the same including reciprocal currencies. +func (p Pair) EqualIncludeReciprocal(cPair Pair) bool { + if (p.Base.Item == cPair.Base.Item && p.Quote.Item == cPair.Quote.Item) || + (p.Base.Item == cPair.Quote.Item && p.Quote.Item == cPair.Base.Item) { + return true + } + return false +} + +// IsCryptoPair checks to see if the pair is a crypto pair e.g. BTCLTC +func (p Pair) IsCryptoPair() bool { + return storage.IsCryptocurrency(p.Base) && + storage.IsCryptocurrency(p.Quote) +} + +// IsCryptoFiatPair checks to see if the pair is a crypto fiat pair e.g. BTCUSD +func (p Pair) IsCryptoFiatPair() bool { + return (storage.IsCryptocurrency(p.Base) && storage.IsFiatCurrency(p.Quote)) || + (storage.IsFiatCurrency(p.Base) && storage.IsCryptocurrency(p.Quote)) +} + +// IsFiatPair checks to see if the pair is a fiat pair e.g. EURUSD +func (p Pair) IsFiatPair() bool { + return storage.IsFiatCurrency(p.Base) && storage.IsFiatCurrency(p.Quote) +} + +// IsInvalid checks invalid pair if base and quote are the same +func (p Pair) IsInvalid() bool { + return p.Base.Item == p.Quote.Item +} + +// Swap turns the currency pair into its reciprocal +func (p Pair) Swap() Pair { + return Pair{Base: p.Quote, Quote: p.Base} +} + +// IsEmpty returns whether or not the pair is empty or is missing a currency +// code +func (p Pair) IsEmpty() bool { + return p.Base.IsEmpty() && p.Quote.IsEmpty() +} + +// ContainsCurrency checks to see if a pair contains a specific currency +func (p Pair) ContainsCurrency(c Code) bool { + return p.Base.Item == c.Item || p.Quote.Item == c.Item +} diff --git a/currency/pair_test.go b/currency/pair_test.go index f429f27a..56511f4f 100644 --- a/currency/pair_test.go +++ b/currency/pair_test.go @@ -12,21 +12,35 @@ const ( func TestLower(t *testing.T) { t.Parallel() - pair := NewPairFromString(defaultPair) + pair, err := NewPairFromString(defaultPair) + if err != nil { + t.Fatal(err) + } actual := pair.Lower() - expected := NewPairFromString(defaultPair).Lower() - if actual != expected { + expected, err := NewPairFromString(defaultPair) + if err != nil { + t.Fatal(err) + } + + if actual.String() != expected.Lower().String() { t.Errorf("Lower(): %s was not equal to expected value: %s", - actual, expected) + actual, + expected.Lower()) } } func TestUpper(t *testing.T) { t.Parallel() - pair := NewPairFromString(defaultPair) + pair, err := NewPairFromString(defaultPair) + if err != nil { + t.Fatal(err) + } actual := pair.Upper() - expected := NewPairFromString(defaultPair) - if actual != expected { + expected, err := NewPairFromString(defaultPair) + if err != nil { + t.Fatal(err) + } + if actual.String() != expected.String() { t.Errorf("Upper(): %s was not equal to expected value: %s", actual, expected) } @@ -34,7 +48,10 @@ func TestUpper(t *testing.T) { func TestPairUnmarshalJSON(t *testing.T) { var unmarshalHere Pair - configPair := NewPairDelimiter("btc_usd", "_") + configPair, err := NewPairDelimiter("btc_usd", "_") + if err != nil { + t.Fatal(err) + } encoded, err := json.Marshal(configPair) if err != nil { @@ -59,9 +76,9 @@ func TestPairUnmarshalJSON(t *testing.T) { func TestPairMarshalJSON(t *testing.T) { quickstruct := struct { - Pair Pair `json:"superPair"` + Pair *Pair `json:"superPair"` }{ - Pair{Base: BTC, Quote: USD, Delimiter: "-"}, + &Pair{Base: BTC, Quote: USD, Delimiter: "-"}, } encoded, err := json.Marshal(quickstruct) @@ -158,7 +175,14 @@ func TestPair(t *testing.T) { func TestDisplay(t *testing.T) { t.Parallel() - pair := NewPairDelimiter(defaultPairWDelimiter, "-") + _, err := NewPairDelimiter(defaultPairWDelimiter, "wow") + if err == nil { + t.Fatal("error cannot be nil") + } + pair, err := NewPairDelimiter(defaultPairWDelimiter, "-") + if err != nil { + t.Fatal(err) + } actual := pair.String() expected := defaultPairWDelimiter if actual != expected { @@ -319,7 +343,24 @@ func TestNewPairWithDelimiter(t *testing.T) { func TestNewPairDelimiter(t *testing.T) { t.Parallel() - pair := NewPairDelimiter(defaultPairWDelimiter, "-") + _, err := NewPairDelimiter("", "") + if err == nil { + t.Fatal("error cannot be nil") + } + _, err = NewPairDelimiter("BTC_USD", "wow") + if err == nil { + t.Fatal("error cannot be nil") + } + + _, err = NewPairDelimiter("BTC_USD", " ") + if err == nil { + t.Fatal("error cannot be nil") + } + + pair, err := NewPairDelimiter(defaultPairWDelimiter, "-") + if err != nil { + t.Fatal(err) + } actual := pair.String() expected := defaultPairWDelimiter if actual != expected { @@ -338,7 +379,10 @@ func TestNewPairDelimiter(t *testing.T) { ) } - pair = NewPairDelimiter("BTC-MOVE-0626", "-") + pair, err = NewPairDelimiter("BTC-MOVE-0626", "-") + if err != nil { + t.Fatal(err) + } actual = pair.String() expected = "BTC-MOVE-0626" if actual != expected { @@ -348,7 +392,10 @@ func TestNewPairDelimiter(t *testing.T) { ) } - pair = NewPairDelimiter("fBTC-USDT", "-") + pair, err = NewPairDelimiter("fBTC-USDT", "-") + if err != nil { + t.Fatal(err) + } actual = pair.String() expected = "fBTC-USDT" if actual != expected { @@ -404,7 +451,10 @@ func TestNewPairFromIndex(t *testing.T) { func TestNewPairFromString(t *testing.T) { t.Parallel() pairStr := defaultPairWDelimiter - pair := NewPairFromString(pairStr) + pair, err := NewPairFromString(pairStr) + if err != nil { + t.Fatal(err) + } actual := pair.String() expected := defaultPairWDelimiter if actual != expected { @@ -415,7 +465,10 @@ func TestNewPairFromString(t *testing.T) { } pairStr = defaultPair - pair = NewPairFromString(pairStr) + pair, err = NewPairFromString(pairStr) + if err != nil { + t.Fatal(err) + } actual = pair.String() expected = defaultPair if actual != expected { @@ -428,23 +481,45 @@ func TestNewPairFromString(t *testing.T) { func TestNewPairFromFormattedPairs(t *testing.T) { t.Parallel() + p1, err := NewPairDelimiter("BTC-USDT", "-") + if err != nil { + t.Fatal(err) + } + p2, err := NewPairDelimiter("LTC-USD", "-") + if err != nil { + t.Fatal(err) + } pairs := Pairs{ - NewPairDelimiter("BTC-USDT", "-"), - NewPairDelimiter("LTC-USD", "-"), + p1, + p2, + } + + p, err := NewPairFromFormattedPairs("BTCUSDT", pairs, PairFormat{ + Uppercase: true, + }) + if err != nil { + t.Fatal(err) } - p := NewPairFromFormattedPairs("BTCUSDT", pairs, PairFormat{Uppercase: true}) if p.String() != "BTC-USDT" { t.Error("TestNewPairFromFormattedPairs: Expected currency was not found") } - p = NewPairFromFormattedPairs("btcusdt", pairs, PairFormat{Uppercase: false}) + p, err = NewPairFromFormattedPairs("btcusdt", pairs, PairFormat{Uppercase: false}) + if err != nil { + t.Fatal(err) + } + if p.String() != "BTC-USDT" { t.Error("TestNewPairFromFormattedPairs: Expected currency was not found") } // Now a wrong one, will default to NewPairFromString - p = NewPairFromFormattedPairs("ethusdt", pairs, PairFormat{}) + p, err = NewPairFromFormattedPairs("ethusdt", pairs, PairFormat{}) + if err != nil { + t.Fatal(err) + } + if p.String() != "ethusdt" && p.Base.String() != "eth" { t.Error("TestNewPairFromFormattedPairs: Expected currency was not found") } @@ -514,29 +589,43 @@ func TestCopyPairFormat(t *testing.T) { t.Error("TestCopyPairFormat: Expected pair was not found") } - result = CopyPairFormat(NewPair(ETH, USD), pairs, true) + np := NewPair(ETH, USD) + result = CopyPairFormat(np, pairs, true) if result.String() != "" { t.Error("TestCopyPairFormat: Unexpected non empty pair returned") } } func TestFindPairDifferences(t *testing.T) { - pairList := NewPairsFromStrings([]string{defaultPairWDelimiter, "ETH-USD", "LTC-USD"}) + pairList, err := NewPairsFromStrings([]string{defaultPairWDelimiter, "ETH-USD", "LTC-USD"}) + if err != nil { + t.Fatal(err) + } + + dash, err := NewPairsFromStrings([]string{"DASH-USD"}) + if err != nil { + t.Fatal(err) + } // Test new pair update - newPairs, removedPairs := pairList.FindDifferences(NewPairsFromStrings([]string{"DASH-USD"})) + newPairs, removedPairs := pairList.FindDifferences(dash) if len(newPairs) != 1 && len(removedPairs) != 3 { t.Error("TestFindPairDifferences: Unexpected values") } + emptyPairsList, err := NewPairsFromStrings([]string{""}) + if err != nil { + t.Fatal(err) + } + // Test that we don't allow empty strings for new pairs - newPairs, removedPairs = pairList.FindDifferences(NewPairsFromStrings([]string{""})) + newPairs, removedPairs = pairList.FindDifferences(emptyPairsList) if len(newPairs) != 0 && len(removedPairs) != 3 { t.Error("TestFindPairDifferences: Unexpected values") } // Test that we don't allow empty strings for new pairs - newPairs, removedPairs = NewPairsFromStrings([]string{""}).FindDifferences(pairList) + newPairs, removedPairs = emptyPairsList.FindDifferences(pairList) if len(newPairs) != 3 && len(removedPairs) != 0 { t.Error("TestFindPairDifferences: Unexpected values") } diff --git a/currency/pair_types.go b/currency/pair_types.go new file mode 100644 index 00000000..263da25f --- /dev/null +++ b/currency/pair_types.go @@ -0,0 +1,12 @@ +package currency + +// Pair holds currency pair information +type Pair struct { + ID string `json:"id"` + Delimiter string `json:"delimiter,omitempty"` + Base Code `json:"base,omitempty"` + Quote Code `json:"quote,omitempty"` +} + +// Pairs defines a list of pairs +type Pairs []Pair diff --git a/currency/pairs.go b/currency/pairs.go index effe2167..8abbd948 100644 --- a/currency/pairs.go +++ b/currency/pairs.go @@ -10,16 +10,21 @@ import ( // NewPairsFromStrings takes in currency pair strings and returns a currency // pair list -func NewPairsFromStrings(pairs []string) Pairs { - var ps Pairs - for _, p := range pairs { - if p == "" { +func NewPairsFromStrings(pairs []string) (Pairs, error) { + var newPairs Pairs + for i := range pairs { + if pairs[i] == "" { continue } - ps = append(ps, NewPairFromString(p)) + newPair, err := NewPairFromString(pairs[i]) + if err != nil { + return nil, err + } + + newPairs = append(newPairs, newPair) } - return ps + return newPairs, nil } // Strings returns a slice of strings referring to each currency pair @@ -79,8 +84,13 @@ func (p *Pairs) UnmarshalJSON(d []byte) error { } var allThePairs Pairs - for _, data := range strings.Split(pairs, ",") { - allThePairs = append(allThePairs, NewPairFromString(data)) + oldPairs := strings.Split(pairs, ",") + for i := range oldPairs { + pair, err := NewPairFromString(oldPairs[i]) + if err != nil { + return err + } + allThePairs = append(allThePairs, pair) } *p = allThePairs @@ -101,21 +111,16 @@ func (p Pairs) Upper() Pairs { return upper } -// Slice exposes the underlying type -func (p Pairs) Slice() []Pair { - return p -} - // Contains checks to see if a specified pair exists inside a currency pair // array func (p Pairs) Contains(check Pair, exact bool) bool { - for _, pair := range p.Slice() { + for i := range p { if exact { - if pair.Equal(check) { + if p[i].Equal(check) { return true } } else { - if pair.EqualIncludeReciprocal(check) { + if p[i].EqualIncludeReciprocal(check) { return true } } @@ -127,11 +132,11 @@ func (p Pairs) Contains(check Pair, exact bool) bool { // and removes it from the list of pairs func (p Pairs) RemovePairsByFilter(filter Code) Pairs { var pairs Pairs - for _, pair := range p.Slice() { - if pair.ContainsCurrency(filter) { + for i := range p { + if p[i].ContainsCurrency(filter) { continue } - pairs = append(pairs, pair) + pairs = append(pairs, p[i]) } return pairs } @@ -167,12 +172,12 @@ func (p Pairs) FindDifferences(pairs Pairs) (newPairs, removedPairs Pairs) { newPairs = append(newPairs, pairs[x]) } } - for _, oldPair := range p { - if oldPair.String() == "" { + for x := range p { + if p[x].String() == "" { continue } - if !pairs.Contains(oldPair, true) { - removedPairs = append(removedPairs, oldPair) + if !pairs.Contains(p[x], true) { + removedPairs = append(removedPairs, p[x]) } } return @@ -188,6 +193,3 @@ func (p Pairs) GetRandomPair() Pair { return p[rand.Intn(pairsLen)] } - -// Pairs defines a list of pairs -type Pairs []Pair diff --git a/currency/pairs_test.go b/currency/pairs_test.go index 1af69a8f..87429817 100644 --- a/currency/pairs_test.go +++ b/currency/pairs_test.go @@ -6,7 +6,10 @@ import ( ) func TestPairsUpper(t *testing.T) { - pairs := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + pairs, err := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + if err != nil { + t.Fatal(err) + } expected := "BTC_USD,BTC_AUD,BTC_LTC" if pairs.Upper().Join() != expected { @@ -16,7 +19,10 @@ func TestPairsUpper(t *testing.T) { } func TestPairsString(t *testing.T) { - pairs := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + pairs, err := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + if err != nil { + t.Fatal(err) + } expected := []string{"btc_usd", "btc_aud", "btc_ltc"} for i, p := range pairs { @@ -28,7 +34,10 @@ func TestPairsString(t *testing.T) { } func TestPairsJoin(t *testing.T) { - pairs := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + pairs, err := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + if err != nil { + t.Fatal(err) + } expected := "btc_usd,btc_aud,btc_ltc" if pairs.Join() != expected { @@ -38,7 +47,10 @@ func TestPairsJoin(t *testing.T) { } func TestPairsFormat(t *testing.T) { - pairs := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + pairs, err := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + if err != nil { + t.Fatal(err) + } expected := "BTC-USD,BTC-AUD,BTC-LTC" if pairs.Format("-", "", true).Join() != expected { @@ -57,7 +69,10 @@ func TestPairsFormat(t *testing.T) { expected, pairs.Format(":", "KRW", true).Join()) } - pairs = NewPairsFromStrings([]string{"DASHKRW", "BTCKRW"}) + pairs, err = NewPairsFromStrings([]string{"DASHKRW", "BTCKRW"}) + if err != nil { + t.Fatal(err) + } expected = "dash-krw,btc-krw" if pairs.Format("-", "KRW", false).Join() != expected { t.Errorf("Pairs Join() error expected %s but received %s", @@ -106,10 +121,15 @@ func TestPairsUnmarshalJSON(t *testing.T) { } func TestPairsMarshalJSON(t *testing.T) { + pairs, err := NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + if err != nil { + t.Fatal(err) + } + quickstruct := struct { Pairs Pairs `json:"soManyPairs"` }{ - Pairs: NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}), + Pairs: pairs, } encoded, err := json.Marshal(quickstruct) @@ -176,12 +196,21 @@ func TestContains(t *testing.T) { var pairs = Pairs{ NewPair(BTC, USD), NewPair(LTC, USD), + NewPair(USD, ZRX), } if !pairs.Contains(NewPair(BTC, USD), true) { t.Errorf("TestContains: Expected pair was not found") } + if pairs.Contains(NewPair(USD, BTC), true) { + t.Errorf("TestContains: Unexpected pair was found") + } + + if !pairs.Contains(NewPair(USD, BTC), false) { + t.Errorf("TestContains: Expected pair was not found") + } + if pairs.Contains(NewPair(ETH, USD), false) { t.Errorf("TestContains: Non-existent pair was found") } diff --git a/currency/storage.go b/currency/storage.go index 43b59f2c..4f5c7d60 100644 --- a/currency/storage.go +++ b/currency/storage.go @@ -23,8 +23,14 @@ func init() { func (s *Storage) SetDefaults() { s.defaultBaseCurrency = USD s.baseCurrency = s.defaultBaseCurrency - s.SetDefaultFiatCurrencies(USD, AUD, EUR, CNY) - s.SetDefaultCryptocurrencies(BTC, LTC, ETH, DOGE, DASH, XRP, XMR) + err := s.SetDefaultFiatCurrencies(USD, AUD, EUR, CNY) + if err != nil { + log.Errorf(log.Global, "Currency Storage: Setting default fiat currencies error: %s", err) + } + err = s.SetDefaultCryptocurrencies(BTC, LTC, ETH, DOGE, DASH, XRP, XMR) + if err != nil { + log.Errorf(log.Global, "Currency Storage: Setting default cryptocurrencies error: %s", err) + } s.SetupConversionRates() s.fiatExchangeMarkets = forexprovider.NewDefaultFXProvider() } @@ -33,8 +39,9 @@ func (s *Storage) SetDefaults() { // dump file and keep foreign exchange rates updated as fast as possible without // triggering rate limiters, it will also run a full cryptocurrency check // through coin market cap and expose analytics for exchange services -func (s *Storage) RunUpdater(overrides BotOverrides, settings *MainConfiguration, filePath string, verbose bool) error { +func (s *Storage) RunUpdater(overrides BotOverrides, settings *MainConfiguration, filePath string) error { s.mtx.Lock() + s.shutdown = make(chan struct{}) if !settings.Cryptocurrencies.HasData() { s.mtx.Unlock() @@ -48,17 +55,17 @@ func (s *Storage) RunUpdater(overrides BotOverrides, settings *MainConfiguration } s.baseCurrency = settings.FiatDisplayCurrency log.Debugf(log.Global, - "Fiat display currency: %s.\n", s.baseCurrency) + "Fiat display currency: %s.\n", + s.baseCurrency) if settings.CryptocurrencyProvider.Enabled { - log.Debugln( - log.Global, + log.Debugln(log.Global, "Setting up currency analysis system with Coinmarketcap...") c := &coinmarketcap.Coinmarketcap{} c.SetDefaults() err := c.Setup(coinmarketcap.Settings{ Name: settings.CryptocurrencyProvider.Name, - Enabled: true, + Enabled: settings.CryptocurrencyProvider.Enabled, AccountPlan: settings.CryptocurrencyProvider.AccountPlan, APIkey: settings.CryptocurrencyProvider.APIkey, Verbose: settings.CryptocurrencyProvider.Verbose, @@ -174,16 +181,34 @@ func (s *Storage) SetupConversionRates() { // SetDefaultFiatCurrencies assigns the default fiat currency list and adds it // to the running list -func (s *Storage) SetDefaultFiatCurrencies(c ...Code) { +func (s *Storage) SetDefaultFiatCurrencies(c ...Code) error { + for i := range c { + err := s.currencyCodes.UpdateCurrency("", c[i].String(), "", 0, Fiat) + if err != nil { + return err + } + } s.defaultFiatCurrencies = append(s.defaultFiatCurrencies, c...) s.fiatCurrencies = append(s.fiatCurrencies, c...) + return nil } // SetDefaultCryptocurrencies assigns the default cryptocurrency list and adds // it to the running list -func (s *Storage) SetDefaultCryptocurrencies(c ...Code) { +func (s *Storage) SetDefaultCryptocurrencies(c ...Code) error { + for i := range c { + err := s.currencyCodes.UpdateCurrency("", + c[i].String(), + "", + 0, + Cryptocurrency) + if err != nil { + return err + } + } s.defaultCryptoCurrencies = append(s.defaultCryptoCurrencies, c...) s.cryptocurrencies = append(s.cryptocurrencies, c...) + return nil } // SetupForexProviders sets up a new instance of the forex providers @@ -228,7 +253,7 @@ func (s *Storage) ForeignExchangeUpdater() { for { select { - case <-s.shutdownC: + case <-s.shutdown: return case <-SeedForeignExchangeTick.C: @@ -248,36 +273,29 @@ func (s *Storage) ForeignExchangeUpdater() { // SeedCurrencyAnalysisData sets a new instance of a coinmarketcap data. func (s *Storage) SeedCurrencyAnalysisData() error { - b, err := ioutil.ReadFile(s.path) - if err != nil { - err = s.FetchCurrencyAnalysisData() + if s.currencyCodes.LastMainUpdate.IsZero() { + b, err := ioutil.ReadFile(s.path) if err != nil { - return s.WriteCurrencyDataToFile(s.path, false) + return s.FetchCurrencyAnalysisData() + } + var f *File + err = json.Unmarshal(b, &f) + if err != nil { + return err + } + err = s.LoadFileCurrencyData(f) + if err != nil { + return err } - - return s.WriteCurrencyDataToFile(s.path, true) - } - - var fromFile File - err = json.Unmarshal(b, &fromFile) - if err != nil { - return err - } - - err = s.LoadFileCurrencyData(&fromFile) - if err != nil { - return err } // Based on update delay update the file - if fromFile.LastMainUpdate.After(fromFile.LastMainUpdate.Add(s.currencyFileUpdateDelay)) || - fromFile.LastMainUpdate.IsZero() { - err = s.FetchCurrencyAnalysisData() + if time.Now().After(s.currencyCodes.LastMainUpdate.Add(s.currencyFileUpdateDelay)) || + s.currencyCodes.LastMainUpdate.IsZero() { + err := s.FetchCurrencyAnalysisData() if err != nil { - return s.WriteCurrencyDataToFile(s.path, false) + return err } - - return s.WriteCurrencyDataToFile(s.path, true) } return nil @@ -304,7 +322,7 @@ func (s *Storage) WriteCurrencyDataToFile(path string, mainUpdate bool) error { if mainUpdate { t := time.Now() - data.LastMainUpdate = t + data.LastMainUpdate = t.Unix() s.currencyCodes.LastMainUpdate = t } @@ -320,41 +338,62 @@ func (s *Storage) WriteCurrencyDataToFile(path string, mainUpdate bool) error { // LoadFileCurrencyData loads currencies into the currency codes func (s *Storage) LoadFileCurrencyData(f *File) error { for i := range f.Contracts { - err := s.currencyCodes.LoadItem(&f.Contracts[i]) + contract := f.Contracts[i] + contract.Role = Contract + err := s.currencyCodes.LoadItem(&contract) if err != nil { return err } } for i := range f.Cryptocurrency { - err := s.currencyCodes.LoadItem(&f.Cryptocurrency[i]) + crypto := f.Cryptocurrency[i] + crypto.Role = Cryptocurrency + err := s.currencyCodes.LoadItem(&crypto) if err != nil { return err } } for i := range f.Token { - err := s.currencyCodes.LoadItem(&f.Token[i]) + token := f.Token[i] + token.Role = Token + err := s.currencyCodes.LoadItem(&token) if err != nil { return err } } for i := range f.FiatCurrency { - err := s.currencyCodes.LoadItem(&f.FiatCurrency[i]) + fiat := f.FiatCurrency[i] + fiat.Role = Fiat + err := s.currencyCodes.LoadItem(&fiat) if err != nil { return err } } for i := range f.UnsetCurrency { - err := s.currencyCodes.LoadItem(&f.UnsetCurrency[i]) + unset := f.UnsetCurrency[i] + unset.Role = Unset + err := s.currencyCodes.LoadItem(&unset) if err != nil { return err } } - s.currencyCodes.LastMainUpdate = f.LastMainUpdate + switch t := f.LastMainUpdate.(type) { + case string: + parseT, err := time.Parse(time.RFC3339Nano, t) + if err != nil { + return err + } + s.currencyCodes.LastMainUpdate = parseT + case float64: + s.currencyCodes.LastMainUpdate = time.Unix(int64(t), 0) + default: + return errors.New("unhandled type conversion for LastMainUpdate time") + } return nil } @@ -372,19 +411,22 @@ func (s *Storage) UpdateCurrencies() error { } if m[x].Platform.Symbol != "" { - err := s.currencyCodes.UpdateToken(m[x].Name, + err = s.currencyCodes.UpdateCurrency(m[x].Name, m[x].Symbol, m[x].Platform.Symbol, - m[x].ID) + m[x].ID, + Token) if err != nil { return err } continue } - err := s.currencyCodes.UpdateCryptocurrency(m[x].Name, + err = s.currencyCodes.UpdateCurrency(m[x].Name, m[x].Symbol, - m[x].ID) + "", + m[x].ID, + Cryptocurrency) if err != nil { return err } @@ -452,8 +494,7 @@ func (s *Storage) GetExchangeRates() (Conversions, error) { func (s *Storage) SeedForeignExchangeRates() error { s.fxRates.mtx.Lock() defer s.fxRates.mtx.Unlock() - rates, err := s.fiatExchangeMarkets.GetCurrencyData( - s.baseCurrency.String(), + rates, err := s.fiatExchangeMarkets.GetCurrencyData(s.baseCurrency.String(), s.fiatCurrencies.Strings()) if err != nil { return err @@ -463,15 +504,7 @@ func (s *Storage) SeedForeignExchangeRates() error { // UpdateForeignExchangeRates sets exchange rates on the FX map func (s *Storage) updateExchangeRates(m map[string]float64) error { - err := s.fxRates.Update(m) - if err != nil { - return err - } - - if s.path != "" { - return s.WriteCurrencyDataToFile(s.path, false) - } - return nil + return s.fxRates.Update(m) } // SetupCryptoProvider sets congiguration parameters and starts a new instance @@ -502,9 +535,9 @@ func (s *Storage) GetTotalMarketCryptocurrencies() (Currencies, error) { // IsDefaultCurrency returns if a currency is a default currency func (s *Storage) IsDefaultCurrency(c Code) bool { - t, _ := GetTranslation(c) for i := range s.defaultFiatCurrencies { - if s.defaultFiatCurrencies[i].Match(c) || s.defaultFiatCurrencies[i].Match(t) { + if s.defaultFiatCurrencies[i].Match(c) || + s.defaultFiatCurrencies[i].Match(GetTranslation(c)) { return true } } @@ -514,9 +547,9 @@ func (s *Storage) IsDefaultCurrency(c Code) bool { // IsDefaultCryptocurrency returns if a cryptocurrency is a default // cryptocurrency func (s *Storage) IsDefaultCryptocurrency(c Code) bool { - t, _ := GetTranslation(c) - for _, d := range s.defaultCryptoCurrencies { - if d.Match(c) || d.Match(t) { + for i := range s.defaultCryptoCurrencies { + if s.defaultCryptoCurrencies[i].Match(c) || + s.defaultCryptoCurrencies[i].Match(GetTranslation(c)) { return true } } @@ -534,9 +567,9 @@ func (s *Storage) IsFiatCurrency(c Code) bool { return false } - t, _ := GetTranslation(c) - for _, d := range s.fiatCurrencies { - if d.Match(c) || d.Match(t) { + for i := range s.fiatCurrencies { + if s.fiatCurrencies[i].Match(c) || + s.fiatCurrencies[i].Match(GetTranslation(c)) { return true } } @@ -555,9 +588,9 @@ func (s *Storage) IsCryptocurrency(c Code) bool { return false } - t, _ := GetTranslation(c) - for _, d := range s.cryptocurrencies { - if d.Match(c) || d.Match(t) { + for i := range s.cryptocurrencies { + if s.cryptocurrencies[i].Match(c) || + s.cryptocurrencies[i].Match(GetTranslation(c)) { return true } } @@ -573,15 +606,12 @@ func (s *Storage) ValidateCode(newCode string) Code { // ValidateFiatCode validates a fiat currency string and returns a currency // code -func (s *Storage) ValidateFiatCode(newCode string) (Code, error) { - c, err := s.currencyCodes.RegisterFiat(newCode) - if err != nil { - return c, err - } +func (s *Storage) ValidateFiatCode(newCode string) Code { + c := s.currencyCodes.RegisterFiat(newCode) if !s.fiatCurrencies.Contains(c) { s.fiatCurrencies = append(s.fiatCurrencies, c) } - return c, nil + return c } // ValidateCryptoCode validates a cryptocurrency string and returns a currency @@ -637,9 +667,9 @@ func (s *Storage) GetBaseCurrency() Code { // UpdateEnabledCryptoCurrencies appends new cryptocurrencies to the enabled // currency list func (s *Storage) UpdateEnabledCryptoCurrencies(c Currencies) { - for _, i := range c { - if !s.cryptocurrencies.Contains(i) { - s.cryptocurrencies = append(s.cryptocurrencies, i) + for i := range c { + if !s.cryptocurrencies.Contains(c[i]) { + s.cryptocurrencies = append(s.cryptocurrencies, c[i]) } } } @@ -647,9 +677,10 @@ func (s *Storage) UpdateEnabledCryptoCurrencies(c Currencies) { // UpdateEnabledFiatCurrencies appends new fiat currencies to the enabled // currency list func (s *Storage) UpdateEnabledFiatCurrencies(c Currencies) { - for _, i := range c { - if !s.fiatCurrencies.Contains(i) && !s.cryptocurrencies.Contains(i) { - s.fiatCurrencies = append(s.fiatCurrencies, i) + for i := range c { + if !s.fiatCurrencies.Contains(c[i]) && + !s.cryptocurrencies.Contains(c[i]) { + s.fiatCurrencies = append(s.fiatCurrencies, c[i]) } } } @@ -709,3 +740,10 @@ func (s *Storage) NewConversion(from, to Code) (Conversion, error) { func (s *Storage) IsVerbose() bool { return s.Verbose } + +// Shutdown shuts down the currency storage system and saves to currency.json +func (s *Storage) Shutdown() error { + close(s.shutdown) + s.wg.Wait() + return s.WriteCurrencyDataToFile(s.path, true) +} diff --git a/currency/storage_test.go b/currency/storage_test.go index b9e562bf..699f3d7d 100644 --- a/currency/storage_test.go +++ b/currency/storage_test.go @@ -6,7 +6,7 @@ func TestRunUpdater(t *testing.T) { var newStorage Storage emptyMainConfig := MainConfiguration{} - err := newStorage.RunUpdater(BotOverrides{}, &emptyMainConfig, "", false) + err := newStorage.RunUpdater(BotOverrides{}, &emptyMainConfig, "") if err == nil { t.Fatal("storage RunUpdater() error cannot be nil") } @@ -16,12 +16,12 @@ func TestRunUpdater(t *testing.T) { FiatDisplayCurrency: USD, } - err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "", false) + err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "") if err == nil { t.Fatal("storage RunUpdater() error cannot be nil") } - err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "/bla", false) + err = newStorage.RunUpdater(BotOverrides{}, &mainConfig, "/bla") if err != nil { t.Fatal("storage RunUpdater() error", err) } diff --git a/currency/storage_types.go b/currency/storage_types.go index 032be5db..64020db0 100644 --- a/currency/storage_types.go +++ b/currency/storage_types.go @@ -56,7 +56,7 @@ type Storage struct { foreignExchangeUpdateDelay time.Duration mtx sync.Mutex wg sync.WaitGroup - shutdownC chan struct{} + shutdown chan struct{} updaterRunning bool Verbose bool } diff --git a/currency/translation.go b/currency/translation.go index c2db7430..5b9075b0 100644 --- a/currency/translation.go +++ b/currency/translation.go @@ -2,12 +2,12 @@ package currency // GetTranslation returns similar strings for a particular currency if not found // returns the code back -func GetTranslation(currency Code) (Code, bool) { +func GetTranslation(currency Code) Code { val, ok := translations[currency] if !ok { - return currency, ok + return currency } - return val, ok + return val } var translations = map[Code]Code{ diff --git a/currency/translation_test.go b/currency/translation_test.go index 5124597b..95415b38 100644 --- a/currency/translation_test.go +++ b/currency/translation_test.go @@ -5,29 +5,21 @@ import "testing" func TestGetTranslation(t *testing.T) { currencyPair := NewPair(BTC, USD) expected := XBT - actual, ok := GetTranslation(currencyPair.Base) - if !ok { - t.Error("GetTranslation: failed to retrieve translation for BTC") - } - + actual := GetTranslation(currencyPair.Base) if expected != actual { t.Error("GetTranslation: translation result was different to expected result") } currencyPair.Base = NEO - _, ok = GetTranslation(currencyPair.Base) - if ok { + actual = GetTranslation(currencyPair.Base) + if actual != currencyPair.Base { t.Error("GetTranslation: no error on non translatable currency") } expected = BTC currencyPair.Base = XBT - actual, ok = GetTranslation(currencyPair.Base) - if !ok { - t.Error("GetTranslation: failed to retrieve translation for BTC") - } - + actual = GetTranslation(currencyPair.Base) if expected != actual { t.Error("GetTranslation: translation result was different to expected result") } diff --git a/docs/ADD_NEW_EXCHANGE.md b/docs/ADD_NEW_EXCHANGE.md index 169c44c8..ae69630a 100644 --- a/docs/ADD_NEW_EXCHANGE.md +++ b/docs/ADD_NEW_EXCHANGE.md @@ -49,7 +49,7 @@ Find out which asset types are supported by the exchange and add them to the pai config.GetDefaultFilePath() ``` -```go +```js { "name": "FTX", "enabled": true, @@ -61,37 +61,35 @@ config.GetDefaultFilePath() "websocketOrderbookBufferLimit": 5, "baseCurrencies": "USD", "currencyPairs": { - "assetTypes": [ - "spot", - "futures" - ], - "pairs": { - "futures": { - "enabled": "BTC-PERP", - "available": "BTC-PERP", - "requestFormat": { - "uppercase": true, - "delimiter": "-" - }, - "configFormat": { - "uppercase": true, - "delimiter": "-" - } + "pairs": { + "futures": { + "assetEnabled": true, + "enabled": "BTC-PERP", + "available": "BTC-PERP", + "requestFormat": { + "uppercase": true, + "delimiter": "-" }, - "spot": { - "enabled": "BTC/USD", - "available": "BTC/USD", - "requestFormat": { - "uppercase": true, - "delimiter": "/" - }, - "configFormat": { - "uppercase": true, - "delimiter": "/" - } + "configFormat": { + "uppercase": true, + "delimiter": "-" + } + }, + "spot": { + "assetEnabled": true, + "enabled": "BTC/USD", + "available": "BTC/USD", + "requestFormat": { + "uppercase": true, + "delimiter": "/" + }, + "configFormat": { + "uppercase": true, + "delimiter": "/" } } - }, + } + }, "api": { "authenticatedSupport": false, "authenticatedWebsocketApiSupport": false, @@ -177,6 +175,16 @@ Similar to the configs, spot support is inbuilt but other asset types will need Delimiter: "-", }, } + + err := f.StoreAssetPairFormat(asset.Spot, spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + err = f.StoreAssetPairFormat(asset.Futures, futures) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } ``` ### Document the addition of the new exchange (FTX exchange is used as an example below): @@ -645,16 +653,6 @@ The currency package contains many helper functions to format and process curren ### Websocket addition if exchange supports it: -#### Add websocket to exchange struct in ftx.go - -```go -// FTX is the overarching type across this package -type FTX struct { - exchange.Base - WebsocketConn *wshandler.WebsocketConnection // Add this line -} -``` - #### Websocket Setup: - Set the websocket url in ftx_websocket.go that is provided in the documentation: @@ -672,27 +670,35 @@ func (f *FTX) WsConnect() error { return errors.New(wshandler.WebsocketNotEnabled) } var dialer websocket.Dialer - err := f.WebsocketConn.Dial(&dialer, http.Header{}) + err := f.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } - f.WebsocketConn.SetupPingHandler(wshandler.WebsocketPingHandler{ + // Can set up custom ping handler per websocket connection. + f.Websocket.Conn.SetupPingHandler(wshandler.WebsocketPingHandler{ MessageType: websocket.PingMessage, Delay: ftxWebsocketTimer, }) if f.Verbose { log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", f.Name) } + // This reader routine is called prior to initiating a subscription for + // efficient processing. go f.wsReadData() if f.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - err := f.WsAuth() + err = f.WsAuth() if err != nil { f.Websocket.DataHandler <- err f.Websocket.SetCanUseAuthenticatedEndpoints(false) } } - f.GenerateDefaultSubscriptions() - return nil + // Generates the default subscription set, based off enabled pairs. + subs, err := f.GenerateDefaultSubscriptions() + if err != nil { + return err + } + // Finally subscribes to each individual channel. + return f.Websocket.SubscribeToChannels(subs) } ``` @@ -700,22 +706,44 @@ func (f *FTX) WsConnect() error { ```go // GenerateDefaultSubscriptions generates default subscription -func (f *FTX) GenerateDefaultSubscriptions() { - var channels = []string{wsTicker, wsTrades, wsOrderbook, wsMarkets, wsFills, wsOrders} - var subscriptions []wshandler.WebsocketChannelSubscription - for a := range f.CurrencyPairs.AssetTypes { - pairs := f.GetEnabledPairs(f.CurrencyPairs.AssetTypes[a]) +func (f *FTX) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: wsMarkets, + }) + // Ranges over available channels, pairs and asset types to produce a full + // subscription list. + var channels = []string{wsTicker, wsTrades, wsOrderbook} + assets := f.GetAssetTypes() + for a := range assets { + pairs, err := f.GetEnabledPairs(assets[a]) + if err != nil { + return nil, err + } for z := range pairs { - newPair := currency.NewPairWithDelimiter(pairs[z].Base.String(), pairs[z].Quote.String(), "-") + newPair := currency.NewPairWithDelimiter(pairs[z].Base.String(), + pairs[z].Quote.String(), + "-") for x := range channels { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: channels[x], - Currency: newPair, - }) + subscriptions = append(subscriptions, + stream.ChannelSubscription{ + Channel: channels[x], + Currency: newPair, + Asset: assets[a], + }) } } } - f.Websocket.SubscribeToChannels(subscriptions) + // Appends authenticated channels to the subscription list + if f.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + var authchan = []string{wsOrders, wsFills} + for x := range authchan { + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: authchan[x], + }) + } + } + return subscriptions, nil } ``` @@ -753,22 +781,47 @@ type WsSub struct { ```go // Subscribe sends a websocket message to receive data from the channel -func (f *FTX) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - var sub WsSub - a, err := f.GetPairAssetType(channelToSubscribe.Currency) - if err != nil { - return err +func (f *FTX) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + // For subscriptions we try to batch as much as possible to limit the amount + // of connection usage but sometimes this is not supported on the exchange + // API. + var errs common.Errors // This is an array of errors useful in the event that one channel subscription errors but we can subscribe to the next iteration. +channels: + for i := range channelsToSubscribe { + // Type we declared above to send via our websocket connection. + var sub WsSub + sub.Channel = channelsToSubscribe[i].Channel + sub.Operation = subscribe + + switch channelsToSubscribe[i].Channel { + case wsFills, wsOrders, wsMarkets: + // Authenticated wsFills && wsOrders or wsMarkets which is a channel subscription for the full set of tradable markets do not need a currency pair association. + default: + a, err := f.GetPairAssetType(channelsToSubscribe[i].Currency) + if err != nil { + errs = append(errs, err) + continue channels + } + // Ensures our outbound currency pair is formatted correctly, sometimes our configuration format is different from what our request format needs to be. + formattedPair, err := f.FormatExchangeCurrency(channelsToSubscribe[i].Currency, a) + if err != nil { + errs = append(errs, err) + continue channels + } + sub.Market = formattedPair.String() + } + err := f.Websocket.Conn.SendJSONMessage(sub) + if err != nil { + errs = append(errs, err) + continue + } + // When we have a successful subscription, we can alert our internal management system of the success. + f.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) } - switch channelToSubscribe.Channel { - case wsFills, wsOrders: - sub.Operation = "subscribe" - sub.Channel = channelToSubscribe.Channel - default: - sub.Operation = "subscribe" - sub.Channel = channelToSubscribe.Channel - sub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String() + if errs != nil { + return errs } - return f.WebsocketConn.SendJSONMessage(sub) + return nil } ``` @@ -782,7 +835,7 @@ Run gocryptotrader with the following settings enabled in config }, "enabled": { "autoPairUpdates": true, - "websocketAPI": true + "websocketAPI": true // <- Change this to true if it is false ``` #### Handle websocket data: @@ -800,13 +853,12 @@ func (f *FTX) wsReadData() { case <-f.Websocket.ShutdownC: return default: - resp, err := f.WebsocketConn.ReadMessage() - if err != nil { - f.Websocket.ReadMessageErrors <- err + resp := f.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return } - f.Websocket.TrafficAlert <- struct{}{} - err = f.wsHandleData(resp.Raw) + + err := f.wsHandleData(resp.Raw) if err != nil { f.Websocket.DataHandler <- err } @@ -963,7 +1015,7 @@ func (f *FTX) WsAuth() error { Time: intNonce, }, } - return f.WebsocketConn.SendJSONMessage(req) + return f.Websocket.Conn.SendJSONMessage(req) } ``` @@ -971,16 +1023,42 @@ func (f *FTX) WsAuth() error { ```go // Unsubscribe sends a websocket message to stop receiving data from the channel -func (f *FTX) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - var unSub WsSub - a, err := f.GetPairAssetType(channelToSubscribe.Currency) - if err != nil { - return err +func (f *FTX) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + // As with subscribing we want to batch as much as possible, but sometimes this cannot be achieved due to API shortfalls. + var errs common.Errors +channels: + for i := range channelsToUnsubscribe { + var unSub WsSub + unSub.Operation = unsubscribe + unSub.Channel = channelsToUnsubscribe[i].Channel + switch channelsToUnsubscribe[i].Channel { + case wsFills, wsOrders, wsMarkets: + default: + a, err := f.GetPairAssetType(channelsToUnsubscribe[i].Currency) + if err != nil { + errs = append(errs, err) + continue channels + } + + formattedPair, err := f.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, a) + if err != nil { + errs = append(errs, err) + continue channels + } + unSub.Market = formattedPair.String() + } + err := f.Websocket.Conn.SendJSONMessage(unSub) + if err != nil { + errs = append(errs, err) + continue + } + // When we have a successful unsubscription, we can alert our internal management system of the success. + f.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) } - unSub.Operation = "unsubscribe" - unSub.Channel = channelToSubscribe.Channel - unSub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String() - return f.WebsocketConn.SendJSONMessage(unSub) + if errs != nil { + return errs + } + return nil } ``` @@ -989,24 +1067,53 @@ func (f *FTX) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscript Add websocket functionality if supported to Setup: ```go - err = f.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: ftxWSURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: f.WsConnect, - Subscriber: f.Subscribe, - UnSubscriber: f.Unsubscribe, - Features: &f.Features.Supports.WebsocketCapabilities, - }) +// Setup takes in the supplied exchange configuration details and sets params +func (f *FTX) Setup(exch *config.ExchangeConfig) error { + if !exch.Enabled { + f.SetEnabled(false) + return nil + } + + err := f.SetupDefaults(exch) if err != nil { return err - } - ``` + } + + // Websocket details setup below + err = f.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: ftxWSURL, // Default ws endpoint so we can roll back via CLI if needed. + ExchangeName: exch.Name, // Sets websocket name to the exchange name. + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: f.WsConnect, // Connector function outlined above. + Subscriber: f.Subscribe, // Subscriber function outlined above. + UnSubscriber: f.Unsubscribe, // Unsubscriber function outlined above. + GenerateSubscriptions: f.GenerateDefaultSubscriptions, // GenerateDefaultSubscriptions function outlined above. + Features: &f.Features.Supports.WebsocketCapabilities, // Defines the capabilities of the websocket outlined in supported features struct. This allows the websocket connection to be flushed appropriately if we have a pair/asset enable/disable change. This is outlined below. + + // Orderbook buffer specific variables for processing orderbook updates via websocket feed. + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + // Other orderbook buffer vars: + // BufferEnabled bool + // SortBuffer bool + // SortBufferByUpdateIDs bool + // UpdateEntriesByID bool + }) + if err != nil { + return err + } + // Sets up a new connection for the websocket, there are two separate connections denoted by the ConnectionSetup struct auth bool. + return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + // RateLimit int64 rudimentary rate limit that sleeps connection in milliseconds before sending designated payload + // Authenticated bool sets if the connection is dedicated for an authenticated websocket stream which can be accessed from the Websocket field variable AuthConn e.g. f.Websocket.AuthConn + }) +} +``` Below are the features supported by FTX API protocol: @@ -1053,32 +1160,35 @@ Below are the features supported by FTX API protocol: Initially the functions return nil or common.ErrNotYetImplemented ```go -// GetWebsocket returns a pointer to the exchange websocket -func (f *FTX) GetWebsocket() (*wshandler.Websocket, error) { - return f.Websocket, nil -} - -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (f *FTX) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - f.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (f *FTX) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - f.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (f *FTX) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return f.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (f *FTX) AuthenticateWebsocket() error { return f.WsAuth() } -``` \ No newline at end of file +``` + + +## Last but not least - Live testing + +### Live testing websocket via [gctcli](../cmd/gctcli/main.go) + +Please test all `websocket` commands below whilst a GoCryptoTrader instance is running and with the exchange websocket setting enabled: + +- `getinfo` to ensure fetching websocket information is possible (that the websocket connection is enabled, connected and is running). +- `disable/enable` to ensure disabling/enabling a websocket connection disconnects/connects accordingly. +- `getsubs` to ensure the subscriptions are in sync with the exchange's config settings or by manual subscriptions added/removed via `gctcli`. +- `setproxy` to ensure that a proxy can be set and resets the websocket connection accordingly. +- `seturl` to ensure that a new websocket URL can be set in the event of an API endpoint change whilst an instance of GoCryptoTrader is already running. + +Please test all `pair` commands to disable and enable different assets types to witness subscriptions and unsubscriptions: + +- `get` to ensure correct enabled and disabled pairs for a supported asset type. +- `disableasset` to ensure disabling of entire asset class and associated unsubscriptions. +- `enableasset` to ensure correct enabling of entire asset class and associated subscriptions. +- `disable` to ensure correct disabling of pair(s) and and associated unsubscriptions. +- `enable` to ensure correct enabling of pair(s) and associated subscriptions. +- `enableall` to ensure correct enabling of all pairs for an asset type and associated subscriptions. +- `disableall` to ensure correct disabling of all pairs for an asset type and associated unsubscriptions. + +## Open a PR + +Submitting a PR is easy and all are welcome additions to the public repository. Submit via github.com/thrasher-corp/gocryptotrader or contact our team via slack for more information. \ No newline at end of file diff --git a/engine/engine.go b/engine/engine.go index e01ec2d8..303c3dbc 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -385,8 +385,7 @@ func (e *Engine) Start() error { CurrencyDelay: e.Config.Currency.CurrencyFileUpdateDuration, FxRateDelay: e.Config.Currency.ForeignExchangeUpdateDuration, }, - e.Settings.DataDir, - e.Settings.Verbose) + e.Settings.DataDir) if err != nil { gctlog.Errorf(gctlog.Global, "Currency updater system failed to start %v", err) } @@ -514,6 +513,10 @@ func (e *Engine) Stop() { } } + if err := currency.ShutdownStorageUpdater(); err != nil { + gctlog.Errorf(gctlog.Global, "Currency storage system. Error: %v", err) + } + if !e.Settings.EnableDryRun { err := e.Config.SaveConfig(e.Settings.ConfigFile, false) if err != nil { diff --git a/engine/events.go b/engine/events.go index 993b5101..f614f9fb 100644 --- a/engine/events.go +++ b/engine/events.go @@ -72,7 +72,7 @@ var Events []*Event // Add adds an event to the Events chain and returns an index/eventID // and an error -func Add(exchange, item string, condition EventConditionParams, currencyPair currency.Pair, asset asset.Item, action string) (int64, error) { +func Add(exchange, item string, condition EventConditionParams, p currency.Pair, a asset.Item, action string) (int64, error) { err := IsValidEvent(exchange, item, condition, action) if err != nil { return 0, err @@ -89,8 +89,8 @@ func Add(exchange, item string, condition EventConditionParams, currencyPair cur evt.Exchange = exchange evt.Item = item evt.Condition = condition - evt.Pair = currencyPair - evt.Asset = asset + evt.Pair = p + evt.Asset = a evt.Action = action evt.Executed = false Events = append(Events, evt) @@ -99,8 +99,8 @@ func Add(exchange, item string, condition EventConditionParams, currencyPair cur // Remove deletes and event by its ID func Remove(eventID int64) bool { - for i, x := range Events { - if x.ID == eventID { + for i := range Events { + if Events[i].ID == eventID { Events = append(Events[:i], Events[i+1:]...) return true } @@ -112,9 +112,8 @@ func Remove(eventID int64) bool { // events that have been executed. func GetEventCounter() (total, executed int) { total = len(Events) - - for _, x := range Events { - if x.Executed { + for i := range Events { + if Events[i].Executed { executed++ } } @@ -126,11 +125,10 @@ func (e *Event) ExecuteAction() bool { if strings.Contains(e.Action, ",") { action := strings.Split(e.Action, ",") if action[0] == ActionSMSNotify { - message := fmt.Sprintf("Event triggered: %s\n", e.String()) if action[1] == "ALL" { Bot.CommsManager.PushEvent(base.Event{ Type: "event", - Message: message, + Message: "Event triggered: " + e.String(), }) } } diff --git a/engine/events_test.go b/engine/events_test.go index ea1d9ce8..5846d3ca 100644 --- a/engine/events_test.go +++ b/engine/events_test.go @@ -138,10 +138,11 @@ func TestProcessTicker(t *testing.T) { // now populate it with a 0 entry tick := ticker.Price{ - Pair: currency.NewPair(currency.BTC, currency.USD), - Last: 0, + Pair: currency.NewPair(currency.BTC, currency.USD), + ExchangeName: e.Exchange, + AssetType: e.Asset, } - if err := ticker.ProcessTicker(e.Exchange, &tick, e.Asset); err != nil { + if err := ticker.ProcessTicker(&tick); err != nil { t.Fatal("unexpected result:", err) } if r := e.processTicker(); r { @@ -150,7 +151,7 @@ func TestProcessTicker(t *testing.T) { // now populate it with a number > 0 tick.Last = 1337 - if err := ticker.ProcessTicker(e.Exchange, &tick, e.Asset); err != nil { + if err := ticker.ProcessTicker(&tick); err != nil { t.Fatal("unexpected result:", err) } if r := e.processTicker(); !r { diff --git a/engine/exchange.go b/engine/exchange.go index e42ca7c3..5c677ef9 100644 --- a/engine/exchange.go +++ b/engine/exchange.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/binance" "github.com/thrasher-corp/gocryptotrader/exchanges/bitfinex" @@ -237,7 +238,11 @@ func LoadExchange(name string, useWG bool, wg *sync.WaitGroup) error { dryrunParamInteraction("enableallpairs") assets := exchCfg.CurrencyPairs.GetAssetTypes() for x := range assets { - pairs := exchCfg.CurrencyPairs.GetPairs(assets[x], false) + var pairs currency.Pairs + pairs, err = exchCfg.CurrencyPairs.GetPairs(assets[x], false) + if err != nil { + return err + } exchCfg.CurrencyPairs.StorePairs(assets[x], pairs, true) } } diff --git a/engine/fake_exchange_test.go b/engine/fake_exchange_test.go index d7f00df6..26905ceb 100644 --- a/engine/fake_exchange_test.go +++ b/engine/fake_exchange_test.go @@ -12,8 +12,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -89,11 +89,11 @@ func (h *FakePassingExchange) FetchTradablePairs(_ asset.Item) ([]string, error) } func (h *FakePassingExchange) UpdateTradablePairs(_ bool) error { return nil } -func (h *FakePassingExchange) GetEnabledPairs(_ asset.Item) currency.Pairs { - return currency.Pairs{} +func (h *FakePassingExchange) GetEnabledPairs(_ asset.Item) (currency.Pairs, error) { + return currency.Pairs{}, nil } -func (h *FakePassingExchange) GetAvailablePairs(_ asset.Item) currency.Pairs { - return currency.Pairs{} +func (h *FakePassingExchange) GetAvailablePairs(_ asset.Item) (currency.Pairs, error) { + return currency.Pairs{}, nil } func (h *FakePassingExchange) FetchAccountInfo() (account.Holdings, error) { return account.Holdings{}, nil @@ -142,6 +142,11 @@ func (h *FakePassingExchange) GetOrderHistory(_ *order.GetOrdersRequest) ([]orde return nil, nil } func (h *FakePassingExchange) GetActiveOrders(_ *order.GetOrdersRequest) ([]order.Detail, error) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + return nil, err + } + return []order.Detail{ { Price: 1337, @@ -153,25 +158,25 @@ func (h *FakePassingExchange) GetActiveOrders(_ *order.GetOrdersRequest) ([]orde Status: order.Active, AssetType: asset.Spot, Date: time.Now(), - Pair: currency.NewPairFromString("BTCUSD"), + Pair: pair, }, }, nil } -func (h *FakePassingExchange) SetHTTPClientUserAgent(_ string) {} -func (h *FakePassingExchange) GetHTTPClientUserAgent() string { return "" } -func (h *FakePassingExchange) SetClientProxyAddress(_ string) error { return nil } -func (h *FakePassingExchange) SupportsWebsocket() bool { return true } -func (h *FakePassingExchange) SupportsREST() bool { return true } -func (h *FakePassingExchange) IsWebsocketEnabled() bool { return true } -func (h *FakePassingExchange) GetWebsocket() (*wshandler.Websocket, error) { return nil, nil } -func (h *FakePassingExchange) SubscribeToWebsocketChannels(_ []wshandler.WebsocketChannelSubscription) error { +func (h *FakePassingExchange) SetHTTPClientUserAgent(_ string) {} +func (h *FakePassingExchange) GetHTTPClientUserAgent() string { return "" } +func (h *FakePassingExchange) SetClientProxyAddress(_ string) error { return nil } +func (h *FakePassingExchange) SupportsWebsocket() bool { return true } +func (h *FakePassingExchange) SupportsREST() bool { return true } +func (h *FakePassingExchange) IsWebsocketEnabled() bool { return true } +func (h *FakePassingExchange) GetWebsocket() (*stream.Websocket, error) { return nil, nil } +func (h *FakePassingExchange) SubscribeToWebsocketChannels(_ []stream.ChannelSubscription) error { return nil } -func (h *FakePassingExchange) UnsubscribeToWebsocketChannels(_ []wshandler.WebsocketChannelSubscription) error { +func (h *FakePassingExchange) UnsubscribeToWebsocketChannels(_ []stream.ChannelSubscription) error { return nil } func (h *FakePassingExchange) AuthenticateWebsocket() error { return nil } -func (h *FakePassingExchange) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { +func (h *FakePassingExchange) GetSubscriptions() ([]stream.ChannelSubscription, error) { return nil, nil } func (h *FakePassingExchange) GetDefaultConfig() (*config.ExchangeConfig, error) { return nil, nil } diff --git a/engine/helpers.go b/engine/helpers.go index cab09c92..93d873fa 100644 --- a/engine/helpers.go +++ b/engine/helpers.go @@ -292,8 +292,7 @@ func MapCurrenciesByExchange(p currency.Pairs, enabledExchangesOnly bool, assetT continue } exchName := Bot.Config.Exchanges[y].Name - success, err := Bot.Config.SupportsPair(exchName, p[x], assetType) - if err != nil || !success { + if !Bot.Config.SupportsPair(exchName, p[x], assetType) { continue } @@ -324,14 +323,10 @@ func GetExchangeNamesByCurrency(p currency.Pair, enabled bool, assetType asset.I } exchName := Bot.Config.Exchanges[x].Name - success, err := Bot.Config.SupportsPair(exchName, p, assetType) - if err != nil { + if !Bot.Config.SupportsPair(exchName, p, assetType) { continue } - - if success { - exchanges = append(exchanges, exchName) - } + exchanges = append(exchanges, exchName) } return exchanges } @@ -404,19 +399,18 @@ func GetRelatableCurrencies(p currency.Pair, incOrig, incUSDT bool) currency.Pai addPair(p) } - first, ok := currency.GetTranslation(p.Base) - if ok { + first := currency.GetTranslation(p.Base) + if first != p.Base { addPair(currency.NewPair(first, p.Quote)) - var second currency.Code - second, ok = currency.GetTranslation(p.Quote) - if ok { + second := currency.GetTranslation(p.Quote) + if second != p.Quote { addPair(currency.NewPair(first, second)) } } - second, ok := currency.GetTranslation(p.Quote) - if ok { + second := currency.GetTranslation(p.Quote) + if second != p.Quote { addPair(currency.NewPair(p.Base, second)) } } @@ -483,8 +477,8 @@ func GetCollatedExchangeAccountInfoByCoin(accounts []account.Holdings) map[curre // GetExchangeHighestPriceByCurrencyPair returns the exchange with the highest // price for a given currency pair and asset type -func GetExchangeHighestPriceByCurrencyPair(p currency.Pair, assetType asset.Item) (string, error) { - result := stats.SortExchangesByPrice(p, assetType, true) +func GetExchangeHighestPriceByCurrencyPair(p currency.Pair, a asset.Item) (string, error) { + result := stats.SortExchangesByPrice(p, a, true) if len(result) == 0 { return "", fmt.Errorf("no stats for supplied currency pair and asset type") } @@ -717,7 +711,14 @@ func GetAllActiveTickers() []EnabledExchangeCurrencies { exchangeTicker.ExchangeName = exchName for y := range assets { - currencies := exchanges[x].GetEnabledPairs(assets[y]) + currencies, err := exchanges[x].GetEnabledPairs(assets[y]) + if err != nil { + log.Errorf(log.ExchangeSys, + "Exchange %s could not retrieve enabled currencies. Err: %s\n", + exchName, + err) + continue + } for z := range currencies { tp, err := exchanges[x].FetchTicker(currencies[z], assets[y]) if err != nil { @@ -739,19 +740,25 @@ func GetAllEnabledExchangeAccountInfo() AllEnabledExchangeAccounts { var response AllEnabledExchangeAccounts exchanges := GetExchanges() for x := range exchanges { - if !exchanges[x].GetAuthenticatedAPISupport(exchange.RestAuthentication) { - if Bot.Settings.Verbose { - log.Debugf(log.ExchangeSys, "GetAllEnabledExchangeAccountInfo: Skippping %s due to disabled authenticated API support.\n", exchanges[x].GetName()) + if exchanges[x] != nil && exchanges[x].IsEnabled() { + if !exchanges[x].GetAuthenticatedAPISupport(exchange.RestAuthentication) { + if Bot.Settings.Verbose { + log.Debugf(log.ExchangeSys, + "GetAllEnabledExchangeAccountInfo: Skippping %s due to disabled authenticated API support.\n", + exchanges[x].GetName()) + } + continue } - continue + accountInfo, err := exchanges[x].FetchAccountInfo() + if err != nil { + log.Errorf(log.ExchangeSys, + "Error encountered retrieving exchange account info for %s. Error %s\n", + exchanges[x].GetName(), + err) + continue + } + response.Data = append(response.Data, accountInfo) } - accountInfo, err := exchanges[x].FetchAccountInfo() - if err != nil { - log.Errorf(log.ExchangeSys, "Error encountered retrieving exchange account info for %s. Error %s\n", - exchanges[x].GetName(), err) - continue - } - response.Data = append(response.Data, accountInfo) } return response } diff --git a/engine/helpers_test.go b/engine/helpers_test.go index b4a83283..30e404d1 100644 --- a/engine/helpers_test.go +++ b/engine/helpers_test.go @@ -171,136 +171,225 @@ func TestGetSpecificAvailablePairs(t *testing.T) { assetType := asset.Spot result := GetSpecificAvailablePairs(true, true, true, false, assetType) - if !result.Contains(currency.NewPairFromStrings("BTC", "USD"), true) { + btsusd, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + + if !result.Contains(btsusd, true) { t.Fatal("Unexpected result") } - if !result.Contains(currency.NewPairFromStrings("BTC", "USDT"), false) { + btcusdt, err := currency.NewPairFromStrings("BTC", "USDT") + if err != nil { + t.Fatal(err) + } + + if !result.Contains(btcusdt, false) { t.Fatal("Unexpected result") } result = GetSpecificAvailablePairs(true, true, false, false, assetType) - if result.Contains(currency.NewPairFromStrings("BTC", "USDT"), false) { + if result.Contains(btcusdt, false) { t.Fatal("Unexpected result") } + ltcbtc, err := currency.NewPairFromStrings("LTC", "BTC") + if err != nil { + t.Fatal(err) + } + result = GetSpecificAvailablePairs(true, false, false, true, assetType) - if !result.Contains(currency.NewPairFromStrings("LTC", "BTC"), false) { + if !result.Contains(ltcbtc, false) { t.Fatal("Unexpected result") } } func TestIsRelatablePairs(t *testing.T) { SetupTestHelpers(t) + xbtusd, err := currency.NewPairFromStrings("XBT", "USD") + if err != nil { + t.Fatal(err) + } + + btcusd, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } // Test relational pairs with similar names - result := IsRelatablePairs(currency.NewPairFromStrings("XBT", "USD"), - currency.NewPairFromStrings("BTC", "USD"), false) + result := IsRelatablePairs(xbtusd, btcusd, false) if !result { t.Fatal("Unexpected result") } // Test relational pairs with similar names reversed - result = IsRelatablePairs(currency.NewPairFromStrings("BTC", "USD"), - currency.NewPairFromStrings("XBT", "USD"), false) + result = IsRelatablePairs(btcusd, xbtusd, false) if !result { t.Fatal("Unexpected result") } + btcusdt, err := currency.NewPairFromStrings("BTC", "USDT") + if err != nil { + t.Fatal(err) + } + // Test relational pairs with similar names but with Tether support disabled - result = IsRelatablePairs(currency.NewPairFromStrings("XBT", "USD"), - currency.NewPairFromStrings("BTC", "USDT"), false) + result = IsRelatablePairs(xbtusd, btcusdt, false) if result { t.Fatal("Unexpected result") } + xbtusdt, err := currency.NewPairFromStrings("XBT", "USDT") + if err != nil { + t.Fatal(err) + } + // Test relational pairs with similar names but with Tether support enabled - result = IsRelatablePairs(currency.NewPairFromStrings("XBT", "USDT"), - currency.NewPairFromStrings("BTC", "USD"), true) + result = IsRelatablePairs(xbtusdt, btcusd, true) if !result { t.Fatal("Unexpected result") } + aeusdt, err := currency.NewPairFromStrings("AE", "USDT") + if err != nil { + t.Fatal(err) + } + + usdtae, err := currency.NewPairDelimiter("USDT-AE", "-") + if err != nil { + t.Fatal(err) + } + // Test relational pairs with different ordering, a delimiter and with // Tether support enabled - result = IsRelatablePairs(currency.NewPairFromStrings("AE", "USDT"), - currency.NewPairDelimiter("USDT-AE", "-"), true) + result = IsRelatablePairs(aeusdt, usdtae, true) if !result { t.Fatal("Unexpected result") } // Test relational pairs with different ordering, a delimiter and with // Tether support disabled - result = IsRelatablePairs(currency.NewPairFromStrings("AE", "USDT"), - currency.NewPairDelimiter("USDT-AE", "-"), false) + result = IsRelatablePairs(aeusdt, usdtae, false) if !result { t.Fatal("Unexpected result") } + xbteur, err := currency.NewPairFromStrings("XBT", "EUR") + if err != nil { + t.Fatal(err) + } + + btcaud, err := currency.NewPairFromStrings("BTC", "AUD") + if err != nil { + t.Fatal(err) + } + // Test relationl pairs with similar names and different fiat currencies - result = IsRelatablePairs(currency.NewPairFromStrings("XBT", "EUR"), - currency.NewPairFromStrings("BTC", "AUD"), false) + result = IsRelatablePairs(xbteur, btcaud, false) if !result { t.Fatal("Unexpected result") } + usdbtc, err := currency.NewPairFromStrings("USD", "BTC") + if err != nil { + t.Fatal(err) + } + + btceur, err := currency.NewPairFromStrings("BTC", "EUR") + if err != nil { + t.Fatal(err) + } + // Test relationl pairs with similar names, different fiat currencies and // with different ordering - result = IsRelatablePairs(currency.NewPairFromStrings("USD", "BTC"), - currency.NewPairFromStrings("BTC", "EUR"), false) + result = IsRelatablePairs(usdbtc, btceur, false) if !result { // Is this really expected result??? t.Fatal("Unexpected result") } // Test relationl pairs with similar names, different fiat currencies and // with Tether enabled - result = IsRelatablePairs(currency.NewPairFromStrings("USD", "BTC"), - currency.NewPairFromStrings("BTC", "USDT"), true) + result = IsRelatablePairs(usdbtc, btcusdt, true) if !result { t.Fatal("Unexpected result") } + ltcbtc, err := currency.NewPairFromStrings("LTC", "BTC") + if err != nil { + t.Fatal(err) + } + + btcltc, err := currency.NewPairFromStrings("BTC", "LTC") + if err != nil { + t.Fatal(err) + } + // Test relationl crypto pairs with similar names - result = IsRelatablePairs(currency.NewPairFromStrings("LTC", "BTC"), - currency.NewPairFromStrings("BTC", "LTC"), false) + result = IsRelatablePairs(ltcbtc, btcltc, false) if !result { t.Fatal("Unexpected result") } + ltceth, err := currency.NewPairFromStrings("LTC", "ETH") + if err != nil { + t.Fatal(err) + } + + btceth, err := currency.NewPairFromStrings("BTC", "ETH") + if err != nil { + t.Fatal(err) + } + // Test relationl crypto pairs with similar different pairs - result = IsRelatablePairs(currency.NewPairFromStrings("LTC", "ETH"), - currency.NewPairFromStrings("BTC", "ETH"), false) + result = IsRelatablePairs(ltceth, btceth, false) if result { t.Fatal("Unexpected result") } // Test relationl crypto pairs with similar different pairs and with USDT // enabled - result = IsRelatablePairs(currency.NewPairFromStrings("USDT", "USD"), - currency.NewPairFromStrings("BTC", "USD"), true) + usdtusd, err := currency.NewPairFromStrings("USDT", "USD") + if err != nil { + t.Fatal(err) + } + + result = IsRelatablePairs(usdtusd, btcusd, true) if result { t.Fatal("Unexpected result") } + xbtltc, err := currency.NewPairFromStrings("XBT", "LTC") + if err != nil { + t.Fatal(err) + } + // Test relationl crypto pairs with with similar names - result = IsRelatablePairs(currency.NewPairFromStrings("XBT", "LTC"), - currency.NewPairFromStrings("BTC", "LTC"), false) + result = IsRelatablePairs(xbtltc, btcltc, false) if !result { t.Fatal("Unexpected result") } + ltcxbt, err := currency.NewPairFromStrings("LTC", "XBT") + if err != nil { + t.Fatal(err) + } + // Test relationl crypto pairs with different ordering and similar names - result = IsRelatablePairs(currency.NewPairFromStrings("LTC", "XBT"), - currency.NewPairFromStrings("BTC", "LTC"), false) + result = IsRelatablePairs(ltcxbt, btcltc, false) if !result { t.Fatal("Unexpected result") } // Test edge case between two pairs when currency translations were causing // non-relational pairs to be relatable - result = IsRelatablePairs(currency.NewPairFromStrings("EUR", "USD"), - currency.NewPairFromStrings("BTC", "USD"), false) + eurusd, err := currency.NewPairFromStrings("EUR", "USD") + if err != nil { + t.Fatal(err) + } + + result = IsRelatablePairs(eurusd, btcusd, false) if result { t.Fatal("Unexpected result") } @@ -308,44 +397,80 @@ func TestIsRelatablePairs(t *testing.T) { func TestGetRelatableCryptocurrencies(t *testing.T) { SetupTestHelpers(t) - p := GetRelatableCryptocurrencies(currency.NewPairFromStrings("BTC", "LTC")) - if p.Contains(currency.NewPairFromStrings("BTC", "LTC"), true) { + btcltc, err := currency.NewPairFromStrings("BTC", "LTC") + if err != nil { + t.Fatal(err) + } + + btcbtc, err := currency.NewPairFromStrings("BTC", "BTC") + if err != nil { + t.Fatal(err) + } + + ltcltc, err := currency.NewPairFromStrings("LTC", "LTC") + if err != nil { + t.Fatal(err) + } + + btceth, err := currency.NewPairFromStrings("BTC", "ETH") + if err != nil { + t.Fatal(err) + } + + p := GetRelatableCryptocurrencies(btcltc) + if p.Contains(btcltc, true) { t.Fatal("Unexpected result") } - if p.Contains(currency.NewPairFromStrings("BTC", "BTC"), true) { + if p.Contains(btcbtc, true) { t.Fatal("Unexpected result") } - if p.Contains(currency.NewPairFromStrings("LTC", "LTC"), true) { + if p.Contains(ltcltc, true) { t.Fatal("Unexpected result") } - if !p.Contains(currency.NewPairFromStrings("BTC", "ETH"), true) { + if !p.Contains(btceth, true) { t.Fatal("Unexpected result") } - p = GetRelatableCryptocurrencies(currency.NewPairFromStrings("BTC", "LTC")) - if p.Contains(currency.NewPairFromStrings("BTC", "LTC"), true) { + p = GetRelatableCryptocurrencies(btcltc) + if p.Contains(btcltc, true) { t.Fatal("Unexpected result") } - if p.Contains(currency.NewPairFromStrings("BTC", "BTC"), true) { + if p.Contains(btcbtc, true) { t.Fatal("Unexpected result") } - if p.Contains(currency.NewPairFromStrings("LTC", "LTC"), true) { + if p.Contains(ltcltc, true) { t.Fatal("Unexpected result") } - if !p.Contains(currency.NewPairFromStrings("BTC", "ETH"), true) { + if !p.Contains(btceth, true) { t.Fatal("Unexpected result") } } func TestGetRelatableFiatCurrencies(t *testing.T) { SetupTestHelpers(t) - p := GetRelatableFiatCurrencies(currency.NewPairFromStrings("BTC", "USD")) - if !p.Contains(currency.NewPairFromStrings("BTC", "EUR"), true) { + + btsusd, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + + btceur, err := currency.NewPairFromStrings("BTC", "EUR") + if err != nil { + t.Fatal(err) + } + + p := GetRelatableFiatCurrencies(btsusd) + if !p.Contains(btceur, true) { t.Fatal("Unexpected result") } - p = GetRelatableFiatCurrencies(currency.NewPairFromStrings("BTC", "USD")) - if !p.Contains(currency.NewPairFromStrings("BTC", "ZAR"), true) { + btczar, err := currency.NewPairFromStrings("BTC", "ZAR") + if err != nil { + t.Fatal(err) + } + + p = GetRelatableFiatCurrencies(btsusd) + if !p.Contains(btczar, true) { t.Fatal("Unexpected result") } } @@ -373,21 +498,36 @@ func TestGetExchangeNamesByCurrency(t *testing.T) { SetupTestHelpers(t) assetType := asset.Spot - result := GetExchangeNamesByCurrency(currency.NewPairFromStrings("BTC", "USD"), + btsusd, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + + btcjpy, err := currency.NewPairFromStrings("BTC", "JPY") + if err != nil { + t.Fatal(err) + } + + blahjpy, err := currency.NewPairFromStrings("blah", "JPY") + if err != nil { + t.Fatal(err) + } + + result := GetExchangeNamesByCurrency(btsusd, true, assetType) if !common.StringDataCompare(result, "Bitstamp") { t.Fatal("Unexpected result") } - result = GetExchangeNamesByCurrency(currency.NewPairFromStrings("BTC", "JPY"), + result = GetExchangeNamesByCurrency(btcjpy, true, assetType) if !common.StringDataCompare(result, "Bitflyer") { t.Fatal("Unexpected result") } - result = GetExchangeNamesByCurrency(currency.NewPairFromStrings("blah", "JPY"), + result = GetExchangeNamesByCurrency(blahjpy, true, assetType) if len(result) > 0 { @@ -415,9 +555,12 @@ func TestGetSpecificOrderbook(t *testing.T) { t.Fatal("Unexpected result", err) } - ob, err := GetSpecificOrderbook(currency.NewPairFromString("BTCUSD"), - "Bitstamp", - asset.Spot) + btsusd, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + + ob, err := GetSpecificOrderbook(btsusd, "Bitstamp", asset.Spot) if err != nil { t.Fatal(err) } @@ -426,9 +569,12 @@ func TestGetSpecificOrderbook(t *testing.T) { t.Fatal("Unexpected result") } - _, err = GetSpecificOrderbook(currency.NewPairFromStrings("ETH", "LTC"), - "Bitstamp", - asset.Spot) + ethltc, err := currency.NewPairFromStrings("ETH", "LTC") + if err != nil { + t.Fatal(err) + } + + _, err = GetSpecificOrderbook(ethltc, "Bitstamp", asset.Spot) if err == nil { t.Fatal("Unexpected result") } @@ -440,16 +586,21 @@ func TestGetSpecificTicker(t *testing.T) { SetupTestHelpers(t) LoadExchange("Bitstamp", false, nil) - p := currency.NewPairFromStrings("BTC", "USD") - err := ticker.ProcessTicker("Bitstamp", - &ticker.Price{Pair: p, Last: 1000}, - asset.Spot) + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + + err = ticker.ProcessTicker(&ticker.Price{ + Pair: p, + Last: 1000, + AssetType: asset.Spot, + ExchangeName: "Bitstamp"}) if err != nil { t.Fatal("ProcessTicker error", err) } - tick, err := GetSpecificTicker(currency.NewPairFromStrings("BTC", "USD"), "Bitstamp", - asset.Spot) + tick, err := GetSpecificTicker(p, "Bitstamp", asset.Spot) if err != nil { t.Fatal(err) } @@ -458,8 +609,12 @@ func TestGetSpecificTicker(t *testing.T) { t.Fatal("Unexpected result") } - _, err = GetSpecificTicker(currency.NewPairFromStrings("ETH", "LTC"), "Bitstamp", - asset.Spot) + ethltc, err := currency.NewPairFromStrings("ETH", "LTC") + if err != nil { + t.Fatal(err) + } + + _, err = GetSpecificTicker(ethltc, "Bitstamp", asset.Spot) if err == nil { t.Fatal("Unexpected result") } @@ -530,7 +685,11 @@ func TestGetCollatedExchangeAccountInfoByCoin(t *testing.T) { func TestGetExchangeHighestPriceByCurrencyPair(t *testing.T) { SetupTestHelpers(t) - p := currency.NewPairFromStrings("BTC", "USD") + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + stats.Add("Bitfinex", p, asset.Spot, 1000, 10000) stats.Add("Bitstamp", p, asset.Spot, 1337, 10000) exchangeName, err := GetExchangeHighestPriceByCurrencyPair(p, asset.Spot) @@ -542,8 +701,12 @@ func TestGetExchangeHighestPriceByCurrencyPair(t *testing.T) { t.Error("Unexpected result") } - _, err = GetExchangeHighestPriceByCurrencyPair(currency.NewPairFromStrings("BTC", "AUD"), - asset.Spot) + btcaud, err := currency.NewPairFromStrings("BTC", "AUD") + if err != nil { + t.Fatal(err) + } + + _, err = GetExchangeHighestPriceByCurrencyPair(btcaud, asset.Spot) if err == nil { t.Error("Unexpected result") } @@ -552,7 +715,11 @@ func TestGetExchangeHighestPriceByCurrencyPair(t *testing.T) { func TestGetExchangeLowestPriceByCurrencyPair(t *testing.T) { SetupTestHelpers(t) - p := currency.NewPairFromStrings("BTC", "USD") + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + stats.Add("Bitfinex", p, asset.Spot, 1000, 10000) stats.Add("Bitstamp", p, asset.Spot, 1337, 10000) exchangeName, err := GetExchangeLowestPriceByCurrencyPair(p, asset.Spot) @@ -564,8 +731,12 @@ func TestGetExchangeLowestPriceByCurrencyPair(t *testing.T) { t.Error("Unexpected result") } - _, err = GetExchangeLowestPriceByCurrencyPair(currency.NewPairFromStrings("BTC", "AUD"), - asset.Spot) + btcaud, err := currency.NewPairFromStrings("BTC", "AUD") + if err != nil { + t.Fatal(err) + } + + _, err = GetExchangeLowestPriceByCurrencyPair(btcaud, asset.Spot) if err == nil { t.Error("Unexpected reuslt") } diff --git a/engine/orders.go b/engine/orders.go index ca91e898..15578ca5 100644 --- a/engine/orders.go +++ b/engine/orders.go @@ -389,18 +389,45 @@ func (o *orderManager) Submit(newOrder *order.Submit) (*orderSubmitResponse, err func (o *orderManager) processOrders() { authExchanges := GetAuthAPISupportedExchanges() for x := range authExchanges { - log.Debugf(log.OrderMgr, "Order manager: Processing orders for exchange %v.", authExchanges[x]) + log.Debugf(log.OrderMgr, + "Order manager: Processing orders for exchange %v.", + authExchanges[x]) + exch := GetExchangeByName(authExchanges[x]) supportedAssets := exch.GetAssetTypes() for y := range supportedAssets { + pairs, err := exch.GetEnabledPairs(supportedAssets[y]) + if err != nil { + log.Errorf(log.OrderMgr, + "Order manager: Unable to get enabled pairs for %s and asset type %s: %s", + authExchanges[x], + supportedAssets[y], + err) + continue + } + + if len(pairs) == 0 { + if Bot.Settings.Verbose { + log.Debugf(log.OrderMgr, + "Order manager: No pairs enabled for %s and asset type %s, skipping...", + authExchanges[x], + supportedAssets[y]) + } + continue + } + req := order.GetOrdersRequest{ Side: order.AnySide, Type: order.AnyType, - Pairs: exch.GetEnabledPairs(supportedAssets[y]), + Pairs: pairs, } result, err := exch.GetActiveOrders(&req) if err != nil { - log.Warnf(log.OrderMgr, "Order manager: Unable to get active orders: %s", err) + log.Warnf(log.OrderMgr, + "Order manager: Unable to get active orders for %s and asset type %s: %s", + authExchanges[x], + supportedAssets[y], + err) continue } diff --git a/engine/orders_test.go b/engine/orders_test.go index 1e8a0838..f799ace1 100644 --- a/engine/orders_test.go +++ b/engine/orders_test.go @@ -254,6 +254,11 @@ func TestCancelOrder(t *testing.T) { t.Error("Expected error due to no order found") } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + cancel := &order.Cancel{ Exchange: fakePassExchange, ID: "TestCancelOrder", @@ -261,7 +266,7 @@ func TestCancelOrder(t *testing.T) { Status: order.New, AssetType: asset.Spot, Date: time.Now(), - Pair: currency.NewPairFromString("BTCUSD"), + Pair: pair, } err = Bot.OrderManager.Cancel(cancel) if err != nil { @@ -326,9 +331,14 @@ func TestSubmit(t *testing.T) { t.Error("Expected error from validation") } + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + Bot.OrderManager.cfg.EnforceLimitConfig = true Bot.OrderManager.cfg.AllowMarketOrders = false - o.Pair = currency.NewPairFromString("BTCUSD") + o.Pair = pair o.AssetType = asset.Spot o.Side = order.Buy o.Amount = 1 @@ -351,8 +361,13 @@ func TestSubmit(t *testing.T) { t.Error("Expected fail due to order exchange not found in allowed list") } + failPair, err := currency.NewPairFromString("BTCAUD") + if err != nil { + t.Fatal(err) + } + Bot.OrderManager.cfg.AllowedExchanges = nil - Bot.OrderManager.cfg.AllowedPairs = currency.Pairs{currency.NewPairFromString("BTCAUD")} + Bot.OrderManager.cfg.AllowedPairs = currency.Pairs{failPair} _, err = Bot.OrderManager.Submit(o) if err == nil { t.Error("Expected fail due to order pair not found in allowed list") diff --git a/engine/restful_server.go b/engine/restful_server.go index f2f3fc02..cd6cb7e1 100644 --- a/engine/restful_server.go +++ b/engine/restful_server.go @@ -66,7 +66,14 @@ func GetAllActiveOrderbooks() []EnabledExchangeOrderbooks { exchangeOB.ExchangeName = exchName for y := range assets { - currencies := exchanges[x].GetEnabledPairs(assets[y]) + currencies, err := exchanges[x].GetEnabledPairs(assets[y]) + if err != nil { + log.Errorf(log.RESTSys, + "Exchange %s could not retrieve enabled currencies. Err: %s\n", + exchName, + err) + continue + } for z := range currencies { ob, err := exchanges[x].FetchOrderbook(currencies[z], assets[y]) if err != nil { diff --git a/engine/routines.go b/engine/routines.go index bc9e60a4..e2d13354 100644 --- a/engine/routines.go +++ b/engine/routines.go @@ -8,12 +8,11 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/stats" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -57,26 +56,29 @@ func printConvertCurrencyFormat(origCurrency currency.Code, origPrice float64) s ) } -func printTickerSummary(result *ticker.Price, p currency.Pair, assetType asset.Item, exchangeName, protocol string, err error) { +func printTickerSummary(result *ticker.Price, protocol string, err error) { if err != nil { - log.Errorf(log.Ticker, "Failed to get %s %s %s %s ticker. Error: %s\n", - exchangeName, + if err == common.ErrNotYetImplemented { + log.Warnf(log.Ticker, "Failed to get %s ticker. Error: %s\n", + protocol, + err) + return + } + log.Errorf(log.Ticker, "Failed to get %s ticker. Error: %s\n", protocol, - p, - assetType, err) return } - stats.Add(exchangeName, p, assetType, result.Last, result.Volume) - if p.Quote.IsFiatCurrency() && - p.Quote != Bot.Config.Currency.FiatDisplayCurrency { - origCurrency := p.Quote.Upper() + stats.Add(result.ExchangeName, result.Pair, result.AssetType, result.Last, result.Volume) + if result.Pair.Quote.IsFiatCurrency() && + result.Pair.Quote != Bot.Config.Currency.FiatDisplayCurrency { + origCurrency := result.Pair.Quote.Upper() log.Infof(log.Ticker, "%s %s %s %s: TICKER: Last %s Ask %s Bid %s High %s Low %s Volume %.8f\n", - exchangeName, + result.ExchangeName, protocol, - FormatCurrency(p), - strings.ToUpper(assetType.String()), + FormatCurrency(result.Pair), + strings.ToUpper(result.AssetType.String()), printConvertCurrencyFormat(origCurrency, result.Last), printConvertCurrencyFormat(origCurrency, result.Ask), printConvertCurrencyFormat(origCurrency, result.Bid), @@ -84,13 +86,13 @@ func printTickerSummary(result *ticker.Price, p currency.Pair, assetType asset.I printConvertCurrencyFormat(origCurrency, result.Low), result.Volume) } else { - if p.Quote.IsFiatCurrency() && - p.Quote == Bot.Config.Currency.FiatDisplayCurrency { + if result.Pair.Quote.IsFiatCurrency() && + result.Pair.Quote == Bot.Config.Currency.FiatDisplayCurrency { log.Infof(log.Ticker, "%s %s %s %s: TICKER: Last %s Ask %s Bid %s High %s Low %s Volume %.8f\n", - exchangeName, + result.ExchangeName, protocol, - FormatCurrency(p), - strings.ToUpper(assetType.String()), + FormatCurrency(result.Pair), + strings.ToUpper(result.AssetType.String()), printCurrencyFormat(result.Last), printCurrencyFormat(result.Ask), printCurrencyFormat(result.Bid), @@ -99,10 +101,10 @@ func printTickerSummary(result *ticker.Price, p currency.Pair, assetType asset.I result.Volume) } else { log.Infof(log.Ticker, "%s %s %s %s: TICKER: Last %.8f Ask %.8f Bid %.8f High %.8f Low %.8f Volume %.8f\n", - exchangeName, + result.ExchangeName, protocol, - FormatCurrency(p), - strings.ToUpper(assetType.String()), + FormatCurrency(result.Pair), + strings.ToUpper(result.AssetType.String()), result.Last, result.Ask, result.Bid, @@ -113,13 +115,16 @@ func printTickerSummary(result *ticker.Price, p currency.Pair, assetType asset.I } } -func printOrderbookSummary(result *orderbook.Base, p currency.Pair, assetType asset.Item, exchangeName, protocol string, err error) { +func printOrderbookSummary(result *orderbook.Base, protocol string, err error) { if err != nil { - log.Errorf(log.OrderBook, "Failed to get %s %s %s orderbook of type %s. Error: %s\n", - exchangeName, + if err == common.ErrNotYetImplemented { + log.Warnf(log.Ticker, "Failed to get %s ticker. Error: %s\n", + protocol, + err) + return + } + log.Errorf(log.OrderBook, "Failed to get %s orderbook. Error: %s\n", protocol, - p, - assetType, err) return } @@ -127,53 +132,53 @@ func printOrderbookSummary(result *orderbook.Base, p currency.Pair, assetType as bidsAmount, bidsValue := result.TotalBidsAmount() asksAmount, asksValue := result.TotalAsksAmount() - if p.Quote.IsFiatCurrency() && - p.Quote != Bot.Config.Currency.FiatDisplayCurrency { - origCurrency := p.Quote.Upper() + if result.Pair.Quote.IsFiatCurrency() && + result.Pair.Quote != Bot.Config.Currency.FiatDisplayCurrency { + origCurrency := result.Pair.Quote.Upper() log.Infof(log.OrderBook, "%s %s %s %s: ORDERBOOK: Bids len: %d Amount: %f %s. Total value: %s Asks len: %d Amount: %f %s. Total value: %s\n", - exchangeName, + result.ExchangeName, protocol, - FormatCurrency(p), - strings.ToUpper(assetType.String()), + FormatCurrency(result.Pair), + strings.ToUpper(result.AssetType.String()), len(result.Bids), bidsAmount, - p.Base, + result.Pair.Base, printConvertCurrencyFormat(origCurrency, bidsValue), len(result.Asks), asksAmount, - p.Base, + result.Pair.Base, printConvertCurrencyFormat(origCurrency, asksValue), ) } else { - if p.Quote.IsFiatCurrency() && - p.Quote == Bot.Config.Currency.FiatDisplayCurrency { + if result.Pair.Quote.IsFiatCurrency() && + result.Pair.Quote == Bot.Config.Currency.FiatDisplayCurrency { log.Infof(log.OrderBook, "%s %s %s %s: ORDERBOOK: Bids len: %d Amount: %f %s. Total value: %s Asks len: %d Amount: %f %s. Total value: %s\n", - exchangeName, + result.ExchangeName, protocol, - FormatCurrency(p), - strings.ToUpper(assetType.String()), + FormatCurrency(result.Pair), + strings.ToUpper(result.AssetType.String()), len(result.Bids), bidsAmount, - p.Base, + result.Pair.Base, printCurrencyFormat(bidsValue), len(result.Asks), asksAmount, - p.Base, + result.Pair.Base, printCurrencyFormat(asksValue), ) } else { log.Infof(log.OrderBook, "%s %s %s %s: ORDERBOOK: Bids len: %d Amount: %f %s. Total value: %f Asks len: %d Amount: %f %s. Total value: %f\n", - exchangeName, + result.ExchangeName, protocol, - FormatCurrency(p), - strings.ToUpper(assetType.String()), + FormatCurrency(result.Pair), + strings.ToUpper(result.AssetType.String()), len(result.Bids), bidsAmount, - p.Base, + result.Pair.Base, bidsValue, len(result.Asks), asksAmount, - p.Base, + result.Pair.Base, asksValue, ) } @@ -212,28 +217,27 @@ func WebsocketRoutine() { ) } - // TO-DO: expose IsConnected() and IsConnecting so this can be simplified - if exchanges[i].IsWebsocketEnabled() { - ws, err := exchanges[i].GetWebsocket() - if err != nil { - log.Errorf( - log.WebsocketMgr, - "Exchange %s GetWebsocket error: %s\n", - exchanges[i].GetName(), - err, - ) - return - } + ws, err := exchanges[i].GetWebsocket() + if err != nil { + log.Errorf( + log.WebsocketMgr, + "Exchange %s GetWebsocket error: %s\n", + exchanges[i].GetName(), + err, + ) + return + } - // Exchange sync manager might have already started ws - // service or is in the process of connecting, so check - if ws.IsConnected() || ws.IsConnecting() { - return - } + // Exchange sync manager might have already started ws + // service or is in the process of connecting, so check + if ws.IsConnected() || ws.IsConnecting() { + return + } - // Data handler routine - go WebsocketDataReceiver(ws) + // Data handler routine + go WebsocketDataReceiver(ws) + if ws.IsEnabled() { err = ws.Connect() if err != nil { log.Errorf(log.WebsocketMgr, "%v\n", err) @@ -254,7 +258,7 @@ var wg sync.WaitGroup // WebsocketDataReceiver handles websocket data coming from a websocket feed // associated with an exchange -func WebsocketDataReceiver(ws *wshandler.Websocket) { +func WebsocketDataReceiver(ws *stream.Websocket) { wg.Add(1) defer wg.Done() @@ -262,7 +266,7 @@ func WebsocketDataReceiver(ws *wshandler.Websocket) { select { case <-shutdowner: return - case data := <-ws.DataHandler: + case data := <-ws.ToRoutine: err := WebsocketDataHandler(ws.GetName(), data) if err != nil { log.Error(log.WebsocketMgr, err) @@ -278,12 +282,13 @@ func WebsocketDataHandler(exchName string, data interface{}) error { return fmt.Errorf("routines.go - exchange %s nil data sent to websocket", exchName) } + switch d := data.(type) { case string: log.Info(log.WebsocketMgr, d) case error: return fmt.Errorf("routines.go exchange %s websocket error - %s", exchName, data) - case wshandler.TradeData: + case stream.TradeData: if Bot.Settings.Verbose { log.Infof(log.WebsocketMgr, "%s websocket %s %s trade updated %+v", exchName, @@ -291,7 +296,7 @@ func WebsocketDataHandler(exchName string, data interface{}) error { d.AssetType, d) } - case wshandler.FundingData: + case stream.FundingData: if Bot.Settings.Verbose { log.Infof(log.WebsocketMgr, "%s websocket %s %s funding updated %+v", exchName, @@ -307,9 +312,9 @@ func WebsocketDataHandler(exchName string, data interface{}) error { SyncItemTicker, nil) } - err := ticker.ProcessTicker(exchName, d, d.AssetType) - printTickerSummary(d, d.Pair, d.AssetType, exchName, "websocket", err) - case wshandler.KlineData: + err := ticker.ProcessTicker(d) + printTickerSummary(d, "websocket", err) + case stream.KlineData: if Bot.Settings.Verbose { log.Infof(log.WebsocketMgr, "%s websocket %s %s kline updated %+v", exchName, @@ -317,22 +322,15 @@ func WebsocketDataHandler(exchName string, data interface{}) error { d.AssetType, d) } - case wshandler.WebsocketOrderbookUpdate: + case *orderbook.Base: if Bot.Settings.EnableExchangeSyncManager && Bot.ExchangeCurrencyPairManager != nil { Bot.ExchangeCurrencyPairManager.update(exchName, d.Pair, - d.Asset, + d.AssetType, SyncItemOrderbook, nil) } - - if Bot.Settings.Verbose { - log.Infof(log.WebsocketMgr, - "%s websocket %s %s orderbook updated", - exchName, - FormatCurrency(d.Pair), - d.Asset) - } + printOrderbookSummary(d, "websocket", nil) case *order.Detail: if !Bot.OrderManager.orderStore.exists(d) { err := Bot.OrderManager.orderStore.Add(d) @@ -356,7 +354,7 @@ func WebsocketDataHandler(exchName string, data interface{}) error { od.UpdateOrderFromModify(d) case order.ClassificationError: return errors.New(d.Error()) - case wshandler.UnhandledMessageWarning: + case stream.UnhandledMessageWarning: log.Warn(log.WebsocketMgr, d.Message) default: if Bot.Settings.Verbose { diff --git a/engine/routines_test.go b/engine/routines_test.go index ac200633..b54d1645 100644 --- a/engine/routines_test.go +++ b/engine/routines_test.go @@ -5,19 +5,16 @@ import ( "testing" "time" + "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) func TestWebsocketDataHandlerProcess(t *testing.T) { - ws := wshandler.New() - err := ws.Setup(&wshandler.WebsocketSetup{Enabled: true}) - if err != nil { - t.Error(err) - } - ws.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() + ws := sharedtestvalues.NewTestWebsocket() go WebsocketDataReceiver(ws) ws.DataHandler <- "string" time.Sleep(time.Second) @@ -36,11 +33,11 @@ func TestHandleData(t *testing.T) { if err == nil { t.Error("Expected nil data error") } - err = WebsocketDataHandler(exchName, wshandler.TradeData{}) + err = WebsocketDataHandler(exchName, stream.TradeData{}) if err != nil { t.Error(err) } - err = WebsocketDataHandler(exchName, wshandler.FundingData{}) + err = WebsocketDataHandler(exchName, stream.FundingData{}) if err != nil { t.Error(err) } @@ -48,11 +45,7 @@ func TestHandleData(t *testing.T) { if err != nil { t.Error(err) } - err = WebsocketDataHandler(exchName, wshandler.KlineData{}) - if err != nil { - t.Error(err) - } - err = WebsocketDataHandler(exchName, wshandler.WebsocketOrderbookUpdate{}) + err = WebsocketDataHandler(exchName, stream.KlineData{}) if err != nil { t.Error(err) } @@ -107,7 +100,9 @@ func TestHandleData(t *testing.T) { t.Error(err) } - err = WebsocketDataHandler(exchName, wshandler.UnhandledMessageWarning{Message: "there's an issue here's a tissue"}) + err = WebsocketDataHandler(exchName, stream.UnhandledMessageWarning{ + Message: "there's an issue here's a tissue"}, + ) if err != nil { t.Error(err) } @@ -124,4 +119,16 @@ func TestHandleData(t *testing.T) { if err.Error() != classificationError.Error() { t.Errorf("Problem formatting error. Expected %v Received %v", classificationError.Error(), err.Error()) } + + err = WebsocketDataHandler(exchName, &orderbook.Base{ + ExchangeName: fakePassExchange, + Pair: currency.NewPair(currency.BTC, currency.USD), + }) + if err != nil { + t.Error(err) + } + err = WebsocketDataHandler(exchName, "this is a test string") + if err != nil { + t.Error(err) + } } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 75d4787d..c66b8b2f 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -2,6 +2,7 @@ package engine import ( "context" + "encoding/json" "errors" "fmt" "io/ioutil" @@ -52,6 +53,11 @@ const ( errDispatchSystem = "dispatch system offline" ) +var ( + errExchangeNotLoaded = errors.New("exchange is not loaded/doesn't exist") + errExchangeBaseNotFound = errors.New("cannot get exchange base") +) + // RPCServer struct type RPCServer struct{} @@ -165,7 +171,7 @@ func StartRPCRESTProxy() { } // GetInfo returns info about the current GoCryptoTrader session -func (s *RPCServer) GetInfo(ctx context.Context, r *gctrpc.GetInfoRequest) (*gctrpc.GetInfoResponse, error) { +func (s *RPCServer) GetInfo(_ context.Context, r *gctrpc.GetInfoRequest) (*gctrpc.GetInfoResponse, error) { d := time.Since(Bot.Uptime) resp := gctrpc.GetInfoResponse{ Uptime: d.String(), @@ -187,24 +193,32 @@ func (s *RPCServer) GetInfo(ctx context.Context, r *gctrpc.GetInfoRequest) (*gct } // GetSubsystems returns a list of subsystems and their status -func (s *RPCServer) GetSubsystems(ctx context.Context, r *gctrpc.GetSubsystemsRequest) (*gctrpc.GetSusbsytemsResponse, error) { +func (s *RPCServer) GetSubsystems(_ context.Context, r *gctrpc.GetSubsystemsRequest) (*gctrpc.GetSusbsytemsResponse, error) { return &gctrpc.GetSusbsytemsResponse{SubsystemsStatus: GetSubsystemsStatus()}, nil } // EnableSubsystem enables a engine subsytem -func (s *RPCServer) EnableSubsystem(ctx context.Context, r *gctrpc.GenericSubsystemRequest) (*gctrpc.GenericSubsystemResponse, error) { +func (s *RPCServer) EnableSubsystem(_ context.Context, r *gctrpc.GenericSubsystemRequest) (*gctrpc.GenericResponse, error) { err := SetSubsystem(r.Subsystem, true) - return &gctrpc.GenericSubsystemResponse{}, err + if err != nil { + return nil, err + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess, + Data: fmt.Sprintf("subsystem %s enabled", r.Subsystem)}, nil } // DisableSubsystem disables a engine subsytem -func (s *RPCServer) DisableSubsystem(ctx context.Context, r *gctrpc.GenericSubsystemRequest) (*gctrpc.GenericSubsystemResponse, error) { +func (s *RPCServer) DisableSubsystem(_ context.Context, r *gctrpc.GenericSubsystemRequest) (*gctrpc.GenericResponse, error) { err := SetSubsystem(r.Subsystem, false) - return &gctrpc.GenericSubsystemResponse{}, err + if err != nil { + return nil, err + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess, + Data: fmt.Sprintf("subsystem %s disabled", r.Subsystem)}, nil } // GetRPCEndpoints returns a list of API endpoints -func (s *RPCServer) GetRPCEndpoints(ctx context.Context, r *gctrpc.GetRPCEndpointsRequest) (*gctrpc.GetRPCEndpointsResponse, error) { +func (s *RPCServer) GetRPCEndpoints(_ context.Context, r *gctrpc.GetRPCEndpointsRequest) (*gctrpc.GetRPCEndpointsResponse, error) { endpoints := GetRPCEndpoints() var resp gctrpc.GetRPCEndpointsResponse resp.Endpoints = make(map[string]*gctrpc.RPCEndpoint) @@ -218,7 +232,7 @@ func (s *RPCServer) GetRPCEndpoints(ctx context.Context, r *gctrpc.GetRPCEndpoin } // GetCommunicationRelayers returns the status of the engines communication relayers -func (s *RPCServer) GetCommunicationRelayers(ctx context.Context, r *gctrpc.GetCommunicationRelayersRequest) (*gctrpc.GetCommunicationRelayersResponse, error) { +func (s *RPCServer) GetCommunicationRelayers(_ context.Context, r *gctrpc.GetCommunicationRelayersRequest) (*gctrpc.GetCommunicationRelayersResponse, error) { relayers, err := Bot.CommsManager.GetStatus() if err != nil { return nil, err @@ -237,38 +251,44 @@ func (s *RPCServer) GetCommunicationRelayers(ctx context.Context, r *gctrpc.GetC // GetExchanges returns a list of exchanges // Param is whether or not you wish to list enabled exchanges -func (s *RPCServer) GetExchanges(ctx context.Context, r *gctrpc.GetExchangesRequest) (*gctrpc.GetExchangesResponse, error) { +func (s *RPCServer) GetExchanges(_ context.Context, r *gctrpc.GetExchangesRequest) (*gctrpc.GetExchangesResponse, error) { exchanges := strings.Join(GetExchangeNames(r.Enabled), ",") return &gctrpc.GetExchangesResponse{Exchanges: exchanges}, nil } // DisableExchange disables an exchange -func (s *RPCServer) DisableExchange(ctx context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GenericExchangeNameResponse, error) { +func (s *RPCServer) DisableExchange(_ context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GenericResponse, error) { err := UnloadExchange(r.Exchange) - return &gctrpc.GenericExchangeNameResponse{}, err + if err != nil { + return nil, err + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess}, nil } // EnableExchange enables an exchange -func (s *RPCServer) EnableExchange(ctx context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GenericExchangeNameResponse, error) { +func (s *RPCServer) EnableExchange(_ context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GenericResponse, error) { err := LoadExchange(r.Exchange, false, nil) - return &gctrpc.GenericExchangeNameResponse{}, err + if err != nil { + return nil, err + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess}, nil } // GetExchangeOTPCode retrieves an exchanges OTP code -func (s *RPCServer) GetExchangeOTPCode(ctx context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GetExchangeOTPReponse, error) { +func (s *RPCServer) GetExchangeOTPCode(_ context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GetExchangeOTPReponse, error) { result, err := GetExchangeoOTPByName(r.Exchange) return &gctrpc.GetExchangeOTPReponse{OtpCode: result}, err } // GetExchangeOTPCodes retrieves OTP codes for all exchanges which have an // OTP secret installed -func (s *RPCServer) GetExchangeOTPCodes(ctx context.Context, r *gctrpc.GetExchangeOTPsRequest) (*gctrpc.GetExchangeOTPsResponse, error) { +func (s *RPCServer) GetExchangeOTPCodes(_ context.Context, r *gctrpc.GetExchangeOTPsRequest) (*gctrpc.GetExchangeOTPsResponse, error) { result, err := GetExchangeOTPs() return &gctrpc.GetExchangeOTPsResponse{OtpCodes: result}, err } // GetExchangeInfo gets info for a specific exchange -func (s *RPCServer) GetExchangeInfo(ctx context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GetExchangeInfoResponse, error) { +func (s *RPCServer) GetExchangeInfo(_ context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GetExchangeInfoResponse, error) { exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange) if err != nil { return nil, err @@ -286,11 +306,16 @@ func (s *RPCServer) GetExchangeInfo(ctx context.Context, r *gctrpc.GenericExchan } resp.SupportedAssets = make(map[string]*gctrpc.PairsSupported) - for x := range exchCfg.CurrencyPairs.AssetTypes { - a := exchCfg.CurrencyPairs.AssetTypes[x] - resp.SupportedAssets[a.String()] = &gctrpc.PairsSupported{ - EnabledPairs: exchCfg.CurrencyPairs.Get(a).Enabled.Join(), - AvailablePairs: exchCfg.CurrencyPairs.Get(a).Available.Join(), + assets := exchCfg.CurrencyPairs.GetAssetTypes() + for i := range assets { + ps, err := exchCfg.CurrencyPairs.Get(assets[i]) + if err != nil { + return nil, err + } + + resp.SupportedAssets[assets[i].String()] = &gctrpc.PairsSupported{ + EnabledPairs: ps.Enabled.Join(), + AvailablePairs: ps.Available.Join(), } } return resp, nil @@ -298,13 +323,12 @@ func (s *RPCServer) GetExchangeInfo(ctx context.Context, r *gctrpc.GenericExchan // GetTicker returns the ticker for a specified exchange, currency pair and // asset type -func (s *RPCServer) GetTicker(ctx context.Context, r *gctrpc.GetTickerRequest) (*gctrpc.TickerResponse, error) { - t, err := GetSpecificTicker( - currency.Pair{ - Delimiter: r.Pair.Delimiter, - Base: currency.NewCode(r.Pair.Base), - Quote: currency.NewCode(r.Pair.Quote), - }, +func (s *RPCServer) GetTicker(_ context.Context, r *gctrpc.GetTickerRequest) (*gctrpc.TickerResponse, error) { + t, err := GetSpecificTicker(currency.Pair{ + Delimiter: r.Pair.Delimiter, + Base: currency.NewCode(r.Pair.Base), + Quote: currency.NewCode(r.Pair.Quote), + }, r.Exchange, asset.Item(r.AssetType), ) @@ -329,7 +353,7 @@ func (s *RPCServer) GetTicker(ctx context.Context, r *gctrpc.GetTickerRequest) ( // GetTickers returns a list of tickers for all enabled exchanges and all // enabled currency pairs -func (s *RPCServer) GetTickers(ctx context.Context, r *gctrpc.GetTickersRequest) (*gctrpc.GetTickersResponse, error) { +func (s *RPCServer) GetTickers(_ context.Context, r *gctrpc.GetTickersRequest) (*gctrpc.GetTickersResponse, error) { activeTickers := GetAllActiveTickers() var tickers []*gctrpc.Tickers @@ -362,13 +386,12 @@ func (s *RPCServer) GetTickers(ctx context.Context, r *gctrpc.GetTickersRequest) // GetOrderbook returns an orderbook for a specific exchange, currency pair // and asset type -func (s *RPCServer) GetOrderbook(ctx context.Context, r *gctrpc.GetOrderbookRequest) (*gctrpc.OrderbookResponse, error) { - ob, err := GetSpecificOrderbook( - currency.Pair{ - Delimiter: r.Pair.Delimiter, - Base: currency.NewCode(r.Pair.Base), - Quote: currency.NewCode(r.Pair.Quote), - }, +func (s *RPCServer) GetOrderbook(_ context.Context, r *gctrpc.GetOrderbookRequest) (*gctrpc.OrderbookResponse, error) { + ob, err := GetSpecificOrderbook(currency.Pair{ + Delimiter: r.Pair.Delimiter, + Base: currency.NewCode(r.Pair.Base), + Quote: currency.NewCode(r.Pair.Quote), + }, r.Exchange, asset.Item(r.AssetType), ) @@ -405,7 +428,7 @@ func (s *RPCServer) GetOrderbook(ctx context.Context, r *gctrpc.GetOrderbookRequ // GetOrderbooks returns a list of orderbooks for all enabled exchanges and all // enabled currency pairs -func (s *RPCServer) GetOrderbooks(ctx context.Context, r *gctrpc.GetOrderbooksRequest) (*gctrpc.GetOrderbooksResponse, error) { +func (s *RPCServer) GetOrderbooks(_ context.Context, r *gctrpc.GetOrderbooksRequest) (*gctrpc.GetOrderbooksResponse, error) { activeOrderbooks := GetAllActiveOrderbooks() var orderbooks []*gctrpc.Orderbooks @@ -448,10 +471,10 @@ func (s *RPCServer) GetOrderbooks(ctx context.Context, r *gctrpc.GetOrderbooksRe } // GetAccountInfo returns an account balance for a specific exchange -func (s *RPCServer) GetAccountInfo(ctx context.Context, r *gctrpc.GetAccountInfoRequest) (*gctrpc.GetAccountInfoResponse, error) { +func (s *RPCServer) GetAccountInfo(_ context.Context, r *gctrpc.GetAccountInfoRequest) (*gctrpc.GetAccountInfoResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } resp, err := exch.FetchAccountInfo() @@ -484,7 +507,7 @@ func (s *RPCServer) GetAccountInfoStream(r *gctrpc.GetAccountInfoRequest, stream exch := GetExchangeByName(r.Exchange) if exch == nil { - return errors.New("exchange is not loaded/doesn't exist") + return errExchangeNotLoaded } initAcc, err := exch.FetchAccountInfo() @@ -558,12 +581,12 @@ func (s *RPCServer) GetAccountInfoStream(r *gctrpc.GetAccountInfoRequest, stream } // GetConfig returns the bots config -func (s *RPCServer) GetConfig(ctx context.Context, r *gctrpc.GetConfigRequest) (*gctrpc.GetConfigResponse, error) { +func (s *RPCServer) GetConfig(_ context.Context, r *gctrpc.GetConfigRequest) (*gctrpc.GetConfigResponse, error) { return &gctrpc.GetConfigResponse{}, common.ErrNotYetImplemented } // GetPortfolio returns the portfolio details -func (s *RPCServer) GetPortfolio(ctx context.Context, r *gctrpc.GetPortfolioRequest) (*gctrpc.GetPortfolioResponse, error) { +func (s *RPCServer) GetPortfolio(_ context.Context, r *gctrpc.GetPortfolioRequest) (*gctrpc.GetPortfolioResponse, error) { var addrs []*gctrpc.PortfolioAddress botAddrs := Bot.Portfolio.Addresses @@ -584,7 +607,7 @@ func (s *RPCServer) GetPortfolio(ctx context.Context, r *gctrpc.GetPortfolioRequ } // GetPortfolioSummary returns the portfolio summary -func (s *RPCServer) GetPortfolioSummary(ctx context.Context, r *gctrpc.GetPortfolioSummaryRequest) (*gctrpc.GetPortfolioSummaryResponse, error) { +func (s *RPCServer) GetPortfolioSummary(_ context.Context, r *gctrpc.GetPortfolioSummaryRequest) (*gctrpc.GetPortfolioSummaryResponse, error) { result := Bot.Portfolio.GetPortfolioSummary() var resp gctrpc.GetPortfolioSummaryResponse @@ -640,22 +663,30 @@ 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) { - err := Bot.Portfolio.AddAddress(r.Address, r.Description, currency.NewCode(r.CoinType), r.Balance) +func (s *RPCServer) AddPortfolioAddress(_ context.Context, r *gctrpc.AddPortfolioAddressRequest) (*gctrpc.GenericResponse, error) { + err := Bot.Portfolio.AddAddress(r.Address, + r.Description, + currency.NewCode(r.CoinType), + r.Balance) if err != nil { return nil, err } - return &gctrpc.AddPortfolioAddressResponse{}, err + return &gctrpc.GenericResponse{Status: MsgStatusSuccess}, nil } // RemovePortfolioAddress removes an address from the portfolio manager -func (s *RPCServer) RemovePortfolioAddress(ctx context.Context, r *gctrpc.RemovePortfolioAddressRequest) (*gctrpc.RemovePortfolioAddressResponse, error) { - err := Bot.Portfolio.RemoveAddress(r.Address, r.Description, currency.NewCode(r.CoinType)) - return &gctrpc.RemovePortfolioAddressResponse{}, err +func (s *RPCServer) RemovePortfolioAddress(_ context.Context, r *gctrpc.RemovePortfolioAddressRequest) (*gctrpc.GenericResponse, error) { + err := Bot.Portfolio.RemoveAddress(r.Address, + r.Description, + currency.NewCode(r.CoinType)) + if err != nil { + return nil, err + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess}, nil } // GetForexProviders returns a list of available forex providers -func (s *RPCServer) GetForexProviders(ctx context.Context, r *gctrpc.GetForexProvidersRequest) (*gctrpc.GetForexProvidersResponse, error) { +func (s *RPCServer) GetForexProviders(_ context.Context, r *gctrpc.GetForexProvidersRequest) (*gctrpc.GetForexProvidersResponse, error) { providers := Bot.Config.GetForexProviders() if len(providers) == 0 { return nil, fmt.Errorf("forex providers is empty") @@ -677,7 +708,7 @@ func (s *RPCServer) GetForexProviders(ctx context.Context, r *gctrpc.GetForexPro } // GetForexRates returns a list of forex rates -func (s *RPCServer) GetForexRates(ctx context.Context, r *gctrpc.GetForexRatesRequest) (*gctrpc.GetForexRatesResponse, error) { +func (s *RPCServer) GetForexRates(_ context.Context, r *gctrpc.GetForexRatesRequest) (*gctrpc.GetForexRatesResponse, error) { rates, err := currency.GetExchangeRates() if err != nil { return nil, err @@ -712,10 +743,10 @@ func (s *RPCServer) GetForexRates(ctx context.Context, r *gctrpc.GetForexRatesRe // GetOrders returns all open orders, filtered by exchange, currency pair or // asset type -func (s *RPCServer) GetOrders(ctx context.Context, r *gctrpc.GetOrdersRequest) (*gctrpc.GetOrdersResponse, error) { +func (s *RPCServer) GetOrders(_ context.Context, r *gctrpc.GetOrdersRequest) (*gctrpc.GetOrdersResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } resp, err := exch.GetActiveOrders(&order.GetOrdersRequest{ @@ -749,10 +780,10 @@ func (s *RPCServer) GetOrders(ctx context.Context, r *gctrpc.GetOrdersRequest) ( } // GetOrder returns order information based on exchange and order ID -func (s *RPCServer) GetOrder(ctx context.Context, r *gctrpc.GetOrderRequest) (*gctrpc.OrderDetails, error) { +func (s *RPCServer) GetOrder(_ context.Context, r *gctrpc.GetOrderRequest) (*gctrpc.OrderDetails, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } result, err := exch.GetOrderInfo(r.OrderId) if err != nil { @@ -791,14 +822,18 @@ func (s *RPCServer) GetOrder(ctx context.Context, r *gctrpc.GetOrderRequest) (*g // SubmitOrder submits an order specified by exchange, currency pair and asset // type -func (s *RPCServer) SubmitOrder(ctx context.Context, r *gctrpc.SubmitOrderRequest) (*gctrpc.SubmitOrderResponse, error) { +func (s *RPCServer) SubmitOrder(_ context.Context, r *gctrpc.SubmitOrderRequest) (*gctrpc.SubmitOrderResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } - p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) - resp, err := Bot.OrderManager.Submit(&order.Submit{ + p, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + if err != nil { + return nil, err + } + + submission := &order.Submit{ Pair: p, Side: order.Side(r.Side), Type: order.Type(r.OrderType), @@ -806,8 +841,9 @@ func (s *RPCServer) SubmitOrder(ctx context.Context, r *gctrpc.SubmitOrderReques Price: r.Price, ClientID: r.ClientId, Exchange: r.Exchange, - }) + } + resp, err := exch.SubmitOrder(submission) if err != nil { return &gctrpc.SubmitOrderResponse{}, err } @@ -820,13 +856,17 @@ func (s *RPCServer) SubmitOrder(ctx context.Context, r *gctrpc.SubmitOrderReques // SimulateOrder simulates an order specified by exchange, currency pair and asset // type -func (s *RPCServer) SimulateOrder(ctx context.Context, r *gctrpc.SimulateOrderRequest) (*gctrpc.SimulateOrderResponse, error) { +func (s *RPCServer) SimulateOrder(_ context.Context, r *gctrpc.SimulateOrderRequest) (*gctrpc.SimulateOrderResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded + } + + p, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + if err != nil { + return nil, err } - p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) o, err := exch.FetchOrderbook(p, asset.Spot) if err != nil { return nil, err @@ -857,13 +897,17 @@ func (s *RPCServer) SimulateOrder(ctx context.Context, r *gctrpc.SimulateOrderRe // WhaleBomb finds the amount required to reach a specific price target for a given exchange, pair // and asset type -func (s *RPCServer) WhaleBomb(ctx context.Context, r *gctrpc.WhaleBombRequest) (*gctrpc.SimulateOrderResponse, error) { +func (s *RPCServer) WhaleBomb(_ context.Context, r *gctrpc.WhaleBombRequest) (*gctrpc.SimulateOrderResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded + } + + p, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + if err != nil { + return nil, err } - p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) o, err := exch.FetchOrderbook(p, asset.Spot) if err != nil { return nil, err @@ -894,35 +938,43 @@ func (s *RPCServer) WhaleBomb(ctx context.Context, r *gctrpc.WhaleBombRequest) ( // CancelOrder cancels an order specified by exchange, currency pair and asset // type -func (s *RPCServer) CancelOrder(ctx context.Context, r *gctrpc.CancelOrderRequest) (*gctrpc.CancelOrderResponse, error) { +func (s *RPCServer) CancelOrder(_ context.Context, r *gctrpc.CancelOrderRequest) (*gctrpc.GenericResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } - err := exch.CancelOrder(&order.Cancel{ + p, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + if err != nil { + return nil, err + } + + err = exch.CancelOrder(&order.Cancel{ AccountID: r.AccountId, ID: r.OrderId, Side: order.Side(r.Side), WalletAddress: r.WalletAddress, - Pair: currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote), + Pair: p, }) - - return &gctrpc.CancelOrderResponse{}, err + if err != nil { + return nil, err + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess, + Data: fmt.Sprintf("order %s cancelled", r.OrderId)}, nil } // CancelAllOrders cancels all orders, filterable by exchange -func (s *RPCServer) CancelAllOrders(ctx context.Context, r *gctrpc.CancelAllOrdersRequest) (*gctrpc.CancelAllOrdersResponse, error) { +func (s *RPCServer) CancelAllOrders(_ context.Context, r *gctrpc.CancelAllOrdersRequest) (*gctrpc.CancelAllOrdersResponse, error) { return &gctrpc.CancelAllOrdersResponse{}, common.ErrNotYetImplemented } // GetEvents returns the stored events list -func (s *RPCServer) GetEvents(ctx context.Context, r *gctrpc.GetEventsRequest) (*gctrpc.GetEventsResponse, error) { +func (s *RPCServer) GetEvents(_ context.Context, r *gctrpc.GetEventsRequest) (*gctrpc.GetEventsResponse, error) { return &gctrpc.GetEventsResponse{}, common.ErrNotYetImplemented } // AddEvent adds an event -func (s *RPCServer) AddEvent(ctx context.Context, r *gctrpc.AddEventRequest) (*gctrpc.AddEventResponse, error) { +func (s *RPCServer) AddEvent(_ context.Context, r *gctrpc.AddEventRequest) (*gctrpc.AddEventResponse, error) { evtCondition := EventConditionParams{ CheckBids: r.ConditionParams.CheckBids, CheckBidsAndAsks: r.ConditionParams.CheckBidsAndAsks, @@ -943,17 +995,20 @@ func (s *RPCServer) AddEvent(ctx context.Context, r *gctrpc.AddEventRequest) (*g } // RemoveEvent removes an event, specified by an event ID -func (s *RPCServer) RemoveEvent(ctx context.Context, r *gctrpc.RemoveEventRequest) (*gctrpc.RemoveEventResponse, error) { - Remove(r.Id) - return &gctrpc.RemoveEventResponse{}, nil +func (s *RPCServer) RemoveEvent(_ context.Context, r *gctrpc.RemoveEventRequest) (*gctrpc.GenericResponse, error) { + if !Remove(r.Id) { + return nil, fmt.Errorf("event %d not removed", r.Id) + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess, + Data: fmt.Sprintf("event %d removed", r.Id)}, nil } // GetCryptocurrencyDepositAddresses returns a list of cryptocurrency deposit // addresses specified by an exchange -func (s *RPCServer) GetCryptocurrencyDepositAddresses(ctx context.Context, r *gctrpc.GetCryptocurrencyDepositAddressesRequest) (*gctrpc.GetCryptocurrencyDepositAddressesResponse, error) { +func (s *RPCServer) GetCryptocurrencyDepositAddresses(_ context.Context, r *gctrpc.GetCryptocurrencyDepositAddressesRequest) (*gctrpc.GetCryptocurrencyDepositAddressesResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } result, err := GetCryptocurrencyDepositAddressesByExchange(r.Exchange) @@ -962,10 +1017,10 @@ func (s *RPCServer) GetCryptocurrencyDepositAddresses(ctx context.Context, r *gc // GetCryptocurrencyDepositAddress returns a cryptocurrency deposit address // specified by exchange and cryptocurrency -func (s *RPCServer) GetCryptocurrencyDepositAddress(ctx context.Context, r *gctrpc.GetCryptocurrencyDepositAddressRequest) (*gctrpc.GetCryptocurrencyDepositAddressResponse, error) { +func (s *RPCServer) GetCryptocurrencyDepositAddress(_ context.Context, r *gctrpc.GetCryptocurrencyDepositAddressRequest) (*gctrpc.GetCryptocurrencyDepositAddressResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } addr, err := GetExchangeCryptocurrencyDepositAddress(r.Exchange, "", currency.NewCode(r.Cryptocurrency)) @@ -974,10 +1029,10 @@ func (s *RPCServer) GetCryptocurrencyDepositAddress(ctx context.Context, r *gctr // WithdrawCryptocurrencyFunds withdraws cryptocurrency funds specified by // exchange -func (s *RPCServer) WithdrawCryptocurrencyFunds(ctx context.Context, r *gctrpc.WithdrawCryptoRequest) (*gctrpc.WithdrawResponse, error) { +func (s *RPCServer) WithdrawCryptocurrencyFunds(_ context.Context, r *gctrpc.WithdrawCryptoRequest) (*gctrpc.WithdrawResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } request := &withdraw.Request{ @@ -1004,17 +1059,20 @@ func (s *RPCServer) WithdrawCryptocurrencyFunds(ctx context.Context, r *gctrpc.W } // WithdrawFiatFunds withdraws fiat funds specified by exchange -func (s *RPCServer) WithdrawFiatFunds(ctx context.Context, r *gctrpc.WithdrawFiatRequest) (*gctrpc.WithdrawResponse, error) { +func (s *RPCServer) WithdrawFiatFunds(_ context.Context, r *gctrpc.WithdrawFiatRequest) (*gctrpc.WithdrawResponse, error) { exch := GetExchangeByName(r.Exchange) if exch == nil { - return nil, errors.New("exchange is not loaded/doesn't exist") + return nil, errExchangeNotLoaded } var bankAccount *banking.Account - bankAccount, err := banking.GetBankAccountByID(r.BankAccountId) if err != nil { - bankAccount, err = exch.GetBase().GetExchangeBankAccounts(r.BankAccountId, r.Currency) + base := exch.GetBase() + if base == nil { + return nil, errExchangeBaseNotFound + } + bankAccount, err = base.GetExchangeBankAccounts(r.BankAccountId, r.Currency) if err != nil { return nil, err } @@ -1041,7 +1099,7 @@ func (s *RPCServer) WithdrawFiatFunds(ctx context.Context, r *gctrpc.WithdrawFia } // WithdrawalEventByID returns previous withdrawal request details -func (s *RPCServer) WithdrawalEventByID(ctx context.Context, r *gctrpc.WithdrawalEventByIDRequest) (*gctrpc.WithdrawalEventByIDResponse, error) { +func (s *RPCServer) WithdrawalEventByID(_ context.Context, r *gctrpc.WithdrawalEventByIDRequest) (*gctrpc.WithdrawalEventByIDResponse, error) { if !Bot.Config.Database.Enabled { return nil, database.ErrDatabaseSupportDisabled } @@ -1103,7 +1161,7 @@ func (s *RPCServer) WithdrawalEventByID(ctx context.Context, r *gctrpc.Withdrawa } // WithdrawalEventsByExchange returns previous withdrawal request details by exchange -func (s *RPCServer) WithdrawalEventsByExchange(ctx context.Context, r *gctrpc.WithdrawalEventsByExchangeRequest) (*gctrpc.WithdrawalEventsByExchangeResponse, error) { +func (s *RPCServer) WithdrawalEventsByExchange(_ context.Context, r *gctrpc.WithdrawalEventsByExchangeRequest) (*gctrpc.WithdrawalEventsByExchangeResponse, error) { if !Bot.Config.Database.Enabled { return nil, database.ErrDatabaseSupportDisabled } @@ -1124,7 +1182,7 @@ func (s *RPCServer) WithdrawalEventsByExchange(ctx context.Context, r *gctrpc.Wi } // WithdrawalEventsByDate returns previous withdrawal request details by exchange -func (s *RPCServer) WithdrawalEventsByDate(ctx context.Context, r *gctrpc.WithdrawalEventsByDateRequest) (*gctrpc.WithdrawalEventsByExchangeResponse, error) { +func (s *RPCServer) WithdrawalEventsByDate(_ context.Context, r *gctrpc.WithdrawalEventsByDateRequest) (*gctrpc.WithdrawalEventsByExchangeResponse, error) { UTCStartTime, err := time.Parse(common.SimpleTimeFormat, r.Start) if err != nil { return nil, err @@ -1143,7 +1201,7 @@ func (s *RPCServer) WithdrawalEventsByDate(ctx context.Context, r *gctrpc.Withdr } // GetLoggerDetails returns a loggers details -func (s *RPCServer) GetLoggerDetails(ctx context.Context, r *gctrpc.GetLoggerDetailsRequest) (*gctrpc.GetLoggerDetailsResponse, error) { +func (s *RPCServer) GetLoggerDetails(_ context.Context, r *gctrpc.GetLoggerDetailsRequest) (*gctrpc.GetLoggerDetailsResponse, error) { levels, err := log.Level(r.Logger) if err != nil { return nil, err @@ -1158,7 +1216,7 @@ func (s *RPCServer) GetLoggerDetails(ctx context.Context, r *gctrpc.GetLoggerDet } // SetLoggerDetails sets a loggers details -func (s *RPCServer) SetLoggerDetails(ctx context.Context, r *gctrpc.SetLoggerDetailsRequest) (*gctrpc.GetLoggerDetailsResponse, error) { +func (s *RPCServer) SetLoggerDetails(_ context.Context, r *gctrpc.SetLoggerDetailsRequest) (*gctrpc.GetLoggerDetailsResponse, error) { levels, err := log.SetLevel(r.Logger, r.Level) if err != nil { return nil, err @@ -1173,7 +1231,7 @@ func (s *RPCServer) SetLoggerDetails(ctx context.Context, r *gctrpc.SetLoggerDet } // GetExchangePairs returns a list of exchange supported assets and related pairs -func (s *RPCServer) GetExchangePairs(ctx context.Context, r *gctrpc.GetExchangePairsRequest) (*gctrpc.GetExchangePairsResponse, error) { +func (s *RPCServer) GetExchangePairs(_ context.Context, r *gctrpc.GetExchangePairsRequest) (*gctrpc.GetExchangePairsResponse, error) { exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange) if err != nil { return nil, err @@ -1188,84 +1246,105 @@ func (s *RPCServer) GetExchangePairs(ctx context.Context, r *gctrpc.GetExchangeP resp.SupportedAssets = make(map[string]*gctrpc.PairsSupported) assetTypes := exchCfg.CurrencyPairs.GetAssetTypes() for x := range assetTypes { - a := assetTypes[x] - if r.Asset != "" && !strings.EqualFold(a.String(), r.Asset) { + if r.Asset != "" && !strings.EqualFold(assetTypes[x].String(), r.Asset) { continue } - resp.SupportedAssets[a.String()] = &gctrpc.PairsSupported{ - AvailablePairs: exchCfg.CurrencyPairs.Get(a).Available.Join(), - EnabledPairs: exchCfg.CurrencyPairs.Get(a).Enabled.Join(), + + ps, err := exchCfg.CurrencyPairs.Get(assetTypes[x]) + if err != nil { + return nil, err + } + + resp.SupportedAssets[assetTypes[x].String()] = &gctrpc.PairsSupported{ + AvailablePairs: ps.Available.Join(), + EnabledPairs: ps.Enabled.Join(), } } return &resp, nil } -// EnableExchangePair enables the specified pair on an exchange -func (s *RPCServer) EnableExchangePair(ctx context.Context, r *gctrpc.ExchangePairRequest) (*gctrpc.GenericExchangeNameResponse, error) { +// SetExchangePair enables/disabled the specified pair(s) on an exchange +func (s *RPCServer) SetExchangePair(_ context.Context, r *gctrpc.SetExchangePairRequest) (*gctrpc.GenericResponse, error) { exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange) if err != nil { return nil, err } - if r.AssetType != "" && - !exchCfg.CurrencyPairs.GetAssetTypes().Contains(asset.Item(r.AssetType)) { + if r.AssetType == "" { + return nil, errors.New("asset type must be specified") + } + + if !exchCfg.CurrencyPairs.GetAssetTypes().Contains(asset.Item(r.AssetType)) { return nil, errors.New("specified asset type does not exist") } - // Default to spot asset type unless set - a := asset.Spot - if r.AssetType != "" { - a = asset.Item(r.AssetType) + a := asset.Item(r.AssetType) + + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + base := exch.GetBase() + if base == nil { + return nil, errExchangeBaseNotFound } pairFmt, err := Bot.Config.GetPairFormat(r.Exchange, a) if err != nil { return nil, err } + var pass bool + var newErrors common.Errors + for i := range r.Pairs { + var p currency.Pair + p, err = currency.NewPairFromStrings(r.Pairs[i].Base, r.Pairs[i].Quote) + if err != nil { + return nil, err + } - p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote).Format( - pairFmt.Delimiter, pairFmt.Uppercase) - err = exchCfg.CurrencyPairs.EnablePair(a, p) - if err != nil { - return nil, err - } - err = GetExchangeByName(r.Exchange).GetBase().CurrencyPairs.EnablePair( - asset.Item(r.AssetType), p) - return &gctrpc.GenericExchangeNameResponse{}, err -} + if r.Enable { + err = exchCfg.CurrencyPairs.EnablePair(a, + p.Format(pairFmt.Delimiter, pairFmt.Uppercase)) + if err != nil { + newErrors = append(newErrors, err) + continue + } + err = base.CurrencyPairs.EnablePair(asset.Item(r.AssetType), p) + if err != nil { + newErrors = append(newErrors, err) + continue + } + pass = true + continue + } -// DisableExchangePair disables the specified pair on an exchange -func (s *RPCServer) DisableExchangePair(ctx context.Context, r *gctrpc.ExchangePairRequest) (*gctrpc.GenericExchangeNameResponse, error) { - exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange) - if err != nil { - return nil, err + err = exchCfg.CurrencyPairs.DisablePair(asset.Item(r.AssetType), + p.Format(pairFmt.Delimiter, pairFmt.Uppercase)) + if err != nil { + newErrors = append(newErrors, err) + continue + } + err = base.CurrencyPairs.DisablePair(asset.Item(r.AssetType), p) + if err != nil { + newErrors = append(newErrors, err) + continue + } + pass = true } - if r.AssetType != "" && - !exchCfg.CurrencyPairs.GetAssetTypes().Contains(asset.Item(r.AssetType)) { - return nil, errors.New("specified asset type does not exist") + if exch.IsWebsocketEnabled() && pass && base.Websocket.IsConnected() { + err = exch.FlushWebsocketChannels() + if err != nil { + newErrors = append(newErrors, err) + } } - // Default to spot asset type unless set - a := asset.Spot - if r.AssetType != "" { - a = asset.Item(r.AssetType) + if newErrors != nil { + return nil, newErrors } - pairFmt, err := Bot.Config.GetPairFormat(r.Exchange, a) - if err != nil { - return nil, err - } - - p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote).Format( - pairFmt.Delimiter, pairFmt.Uppercase) - err = exchCfg.CurrencyPairs.DisablePair(asset.Item(r.AssetType), p) - if err != nil { - return nil, err - } - err = GetExchangeByName(r.Exchange).GetBase().CurrencyPairs.DisablePair( - asset.Item(r.AssetType), p) - return &gctrpc.GenericExchangeNameResponse{}, err + return &gctrpc.GenericResponse{Status: MsgStatusSuccess}, nil } // GetOrderbookStream streams the requested updated orderbook @@ -1282,7 +1361,10 @@ func (s *RPCServer) GetOrderbookStream(r *gctrpc.GetOrderbookStreamRequest, stre return errors.New(errAssetTypeUnset) } - p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + p, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + if err != nil { + return err + } pipe, err := orderbook.SubscribeOrderbook(r.Exchange, p, asset.Item(r.AssetType)) if err != nil { @@ -1388,7 +1470,10 @@ func (s *RPCServer) GetTickerStream(r *gctrpc.GetTickerStreamRequest, stream gct return errors.New(errAssetTypeUnset) } - p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + p, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + if err != nil { + return err + } pipe, err := ticker.SubscribeTicker(r.Exchange, p, asset.Item(r.AssetType)) if err != nil { @@ -1465,7 +1550,7 @@ func (s *RPCServer) GetExchangeTickerStream(r *gctrpc.GetExchangeTickerStreamReq } // GetAuditEvent returns matching audit events from database -func (s *RPCServer) GetAuditEvent(ctx context.Context, r *gctrpc.GetAuditEventRequest) (*gctrpc.GetAuditEventResponse, error) { +func (s *RPCServer) GetAuditEvent(_ context.Context, r *gctrpc.GetAuditEventRequest) (*gctrpc.GetAuditEventResponse, error) { UTCStartTime, err := time.Parse(common.SimpleTimeFormat, r.StartDate) if err != nil { return nil, err @@ -1513,7 +1598,7 @@ func (s *RPCServer) GetAuditEvent(ctx context.Context, r *gctrpc.GetAuditEventRe } // GetHistoricCandles returns historical candles for a given exchange -func (s *RPCServer) GetHistoricCandles(ctx context.Context, req *gctrpc.GetHistoricCandlesRequest) (*gctrpc.GetHistoricCandlesResponse, error) { +func (s *RPCServer) GetHistoricCandles(_ context.Context, req *gctrpc.GetHistoricCandlesRequest) (*gctrpc.GetHistoricCandlesResponse, error) { if req.Exchange == "" { return nil, errors.New(errExchangeNameUnset) } @@ -1577,7 +1662,7 @@ func (s *RPCServer) GetHistoricCandles(ctx context.Context, req *gctrpc.GetHisto } // GCTScriptStatus returns a slice of current running scripts that includes next run time and uuid -func (s *RPCServer) GCTScriptStatus(ctx context.Context, r *gctrpc.GCTScriptStatusRequest) (*gctrpc.GCTScriptStatusResponse, error) { +func (s *RPCServer) GCTScriptStatus(_ context.Context, r *gctrpc.GCTScriptStatusRequest) (*gctrpc.GCTScriptStatusResponse, error) { if !gctscript.GCTScriptConfig.Enabled { return &gctrpc.GCTScriptStatusResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil } @@ -1605,7 +1690,7 @@ func (s *RPCServer) GCTScriptStatus(ctx context.Context, r *gctrpc.GCTScriptStat } // GCTScriptQuery queries a running script and returns script running information -func (s *RPCServer) GCTScriptQuery(ctx context.Context, r *gctrpc.GCTScriptQueryRequest) (*gctrpc.GCTScriptQueryResponse, error) { +func (s *RPCServer) GCTScriptQuery(_ context.Context, r *gctrpc.GCTScriptQueryRequest) (*gctrpc.GCTScriptQueryResponse, error) { if !gctscript.GCTScriptConfig.Enabled { return &gctrpc.GCTScriptQueryResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil } @@ -1636,9 +1721,9 @@ func (s *RPCServer) GCTScriptQuery(ctx context.Context, r *gctrpc.GCTScriptQuery } // GCTScriptExecute execute a script -func (s *RPCServer) GCTScriptExecute(ctx context.Context, r *gctrpc.GCTScriptExecuteRequest) (*gctrpc.GCTScriptGenericResponse, error) { +func (s *RPCServer) GCTScriptExecute(_ context.Context, r *gctrpc.GCTScriptExecuteRequest) (*gctrpc.GenericResponse, error) { if !gctscript.GCTScriptConfig.Enabled { - return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil + return &gctrpc.GenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil } if r.Script.Path == "" { @@ -1647,13 +1732,13 @@ func (s *RPCServer) GCTScriptExecute(ctx context.Context, r *gctrpc.GCTScriptExe gctVM := gctscript.New() if gctVM == nil { - return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusError, Data: "unable to create VM instance"}, nil + return &gctrpc.GenericResponse{Status: MsgStatusError, Data: "unable to create VM instance"}, nil } script := filepath.Join(r.Script.Path, r.Script.Name) err := gctVM.Load(script) if err != nil { - return &gctrpc.GCTScriptGenericResponse{ + return &gctrpc.GenericResponse{ Status: MsgStatusError, Data: err.Error(), }, nil @@ -1661,21 +1746,21 @@ func (s *RPCServer) GCTScriptExecute(ctx context.Context, r *gctrpc.GCTScriptExe go gctVM.CompileAndRun() - return &gctrpc.GCTScriptGenericResponse{ + return &gctrpc.GenericResponse{ Status: MsgStatusOK, Data: gctVM.ShortName() + " (" + gctVM.ID.String() + ") executed", }, nil } // GCTScriptStop terminate a running script -func (s *RPCServer) GCTScriptStop(ctx context.Context, r *gctrpc.GCTScriptStopRequest) (*gctrpc.GCTScriptGenericResponse, error) { +func (s *RPCServer) GCTScriptStop(_ context.Context, r *gctrpc.GCTScriptStopRequest) (*gctrpc.GenericResponse, error) { if !gctscript.GCTScriptConfig.Enabled { - return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil + return &gctrpc.GenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil } UUID, err := uuid.FromString(r.Script.UUID) if err != nil { - return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusError, Data: err.Error()}, nil + return &gctrpc.GenericResponse{Status: MsgStatusError, Data: err.Error()}, nil } if v, f := gctscript.AllVMSync.Load(UUID); f { @@ -1684,15 +1769,15 @@ func (s *RPCServer) GCTScriptStop(ctx context.Context, r *gctrpc.GCTScriptStopRe if err != nil { status = " " + err.Error() } - return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusOK, Data: v.(*gctscript.VM).ID.String() + status}, nil + return &gctrpc.GenericResponse{Status: MsgStatusOK, Data: v.(*gctscript.VM).ID.String() + status}, nil } - return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusError, Data: "no running script found"}, nil + return &gctrpc.GenericResponse{Status: MsgStatusError, Data: "no running script found"}, nil } // GCTScriptUpload upload a new script to ScriptPath -func (s *RPCServer) GCTScriptUpload(ctx context.Context, r *gctrpc.GCTScriptUploadRequest) (*gctrpc.GCTScriptGenericResponse, error) { +func (s *RPCServer) GCTScriptUpload(_ context.Context, r *gctrpc.GCTScriptUploadRequest) (*gctrpc.GenericResponse, error) { if !gctscript.GCTScriptConfig.Enabled { - return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil + return &gctrpc.GenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil } fPath := filepath.Join(gctscript.ScriptPath, r.ScriptName) @@ -1743,7 +1828,7 @@ func (s *RPCServer) GCTScriptUpload(ctx context.Context, r *gctrpc.GCTScriptUplo files, errExtract := archive.UnZip(fPath, filepath.Join(gctscript.ScriptPath, r.ScriptName[:len(r.ScriptName)-4])) if errExtract != nil { log.Errorf(log.Global, "Failed to archive zip file %v", errExtract) - return &gctrpc.GCTScriptGenericResponse{Status: MsgStatusError, Data: errExtract.Error()}, nil + return &gctrpc.GenericResponse{Status: MsgStatusError, Data: errExtract.Error()}, nil } var failedFiles []string for x := range files { @@ -1761,7 +1846,7 @@ func (s *RPCServer) GCTScriptUpload(ctx context.Context, r *gctrpc.GCTScriptUplo if err != nil { log.Errorf(log.GCTScriptMgr, "Failed to remove file %v (%v), manual deletion required", filepath.Base(fPath), err) } - return &gctrpc.GCTScriptGenericResponse{Status: ErrScriptFailedValidation, Data: strings.Join(failedFiles, ", ")}, nil + return &gctrpc.GenericResponse{Status: ErrScriptFailedValidation, Data: strings.Join(failedFiles, ", ")}, nil } } else { err = gctscript.Validate(fPath) @@ -1770,18 +1855,18 @@ func (s *RPCServer) GCTScriptUpload(ctx context.Context, r *gctrpc.GCTScriptUplo if errRemove != nil { log.Errorf(log.GCTScriptMgr, "Failed to remove file %v, manual deletion required: %v", filepath.Base(fPath), errRemove) } - return &gctrpc.GCTScriptGenericResponse{Status: ErrScriptFailedValidation, Data: err.Error()}, nil + return &gctrpc.GenericResponse{Status: ErrScriptFailedValidation, Data: err.Error()}, nil } } - return &gctrpc.GCTScriptGenericResponse{ + return &gctrpc.GenericResponse{ Status: MsgStatusOK, Data: fmt.Sprintf("script %s written", newFile.Name()), }, nil } // GCTScriptReadScript read a script and return contents -func (s *RPCServer) GCTScriptReadScript(ctx context.Context, r *gctrpc.GCTScriptReadScriptRequest) (*gctrpc.GCTScriptQueryResponse, error) { +func (s *RPCServer) GCTScriptReadScript(_ context.Context, r *gctrpc.GCTScriptReadScriptRequest) (*gctrpc.GCTScriptQueryResponse, error) { if !gctscript.GCTScriptConfig.Enabled { return &gctrpc.GCTScriptQueryResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil } @@ -1832,39 +1917,296 @@ func (s *RPCServer) GCTScriptListAll(context.Context, *gctrpc.GCTScriptListAllRe } // GCTScriptStopAll stops all running scripts -func (s *RPCServer) GCTScriptStopAll(context.Context, *gctrpc.GCTScriptStopAllRequest) (*gctrpc.GCTScriptGenericResponse, error) { +func (s *RPCServer) GCTScriptStopAll(context.Context, *gctrpc.GCTScriptStopAllRequest) (*gctrpc.GenericResponse, error) { if !gctscript.GCTScriptConfig.Enabled { - return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil + return &gctrpc.GenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil } err := gctscript.ShutdownAll() if err != nil { - return &gctrpc.GCTScriptGenericResponse{Status: "error", Data: err.Error()}, nil + return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil } - return &gctrpc.GCTScriptGenericResponse{ + return &gctrpc.GenericResponse{ Status: MsgStatusOK, Data: "all running scripts have been stopped", }, nil } // GCTScriptAutoLoadToggle adds or removes an entry to the autoload list -func (s *RPCServer) GCTScriptAutoLoadToggle(ctx context.Context, r *gctrpc.GCTScriptAutoLoadRequest) (*gctrpc.GCTScriptGenericResponse, error) { +func (s *RPCServer) GCTScriptAutoLoadToggle(_ context.Context, r *gctrpc.GCTScriptAutoLoadRequest) (*gctrpc.GenericResponse, error) { if !gctscript.GCTScriptConfig.Enabled { - return &gctrpc.GCTScriptGenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil + return &gctrpc.GenericResponse{Status: gctscript.ErrScriptingDisabled.Error()}, nil } if r.Status { err := gctscript.Autoload(r.Script, true) if err != nil { - return &gctrpc.GCTScriptGenericResponse{Status: "error", Data: err.Error()}, nil + return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil } - return &gctrpc.GCTScriptGenericResponse{Status: "success", Data: "script " + r.Script + " removed from autoload list"}, nil + return &gctrpc.GenericResponse{Status: "success", Data: "script " + r.Script + " removed from autoload list"}, nil } err := gctscript.Autoload(r.Script, false) if err != nil { - return &gctrpc.GCTScriptGenericResponse{Status: "error", Data: err.Error()}, nil + return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil } - return &gctrpc.GCTScriptGenericResponse{Status: "success", Data: "script " + r.Script + " added to autoload list"}, nil + return &gctrpc.GenericResponse{Status: "success", Data: "script " + r.Script + " added to autoload list"}, nil +} + +// SetExchangeAsset enables or disables an exchanges asset type +func (s *RPCServer) SetExchangeAsset(_ context.Context, r *gctrpc.SetExchangeAssetRequest) (*gctrpc.GenericResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange) + if err != nil { + return nil, err + } + + base := exch.GetBase() + if base == nil { + return nil, errExchangeBaseNotFound + } + + if r.Asset == "" { + return nil, errors.New("asset type must be specified") + } + + a := asset.Item(r.Asset) + err = base.CurrencyPairs.SetAssetEnabled(a, r.Enable) + if err != nil { + return nil, err + } + err = exchCfg.CurrencyPairs.SetAssetEnabled(a, r.Enable) + if err != nil { + return nil, err + } + + return &gctrpc.GenericResponse{Status: MsgStatusSuccess}, nil +} + +// SetAllExchangePairs enables or disables an exchanges pairs +func (s *RPCServer) SetAllExchangePairs(_ context.Context, r *gctrpc.SetExchangeAllPairsRequest) (*gctrpc.GenericResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange) + if err != nil { + return nil, err + } + + base := exch.GetBase() + if base == nil { + return nil, errExchangeBaseNotFound + } + + assets := base.CurrencyPairs.GetAssetTypes() + + if r.Enable { + for i := range assets { + var pairs currency.Pairs + pairs, err = base.CurrencyPairs.GetPairs(assets[i], false) + if err != nil { + return nil, err + } + exchCfg.CurrencyPairs.StorePairs(assets[i], pairs, true) + base.CurrencyPairs.StorePairs(assets[i], pairs, true) + } + } else { + for i := range assets { + exchCfg.CurrencyPairs.StorePairs(assets[i], nil, true) + base.CurrencyPairs.StorePairs(assets[i], nil, true) + } + } + + if exch.IsWebsocketEnabled() && base.Websocket.IsConnected() { + err = exch.FlushWebsocketChannels() + if err != nil { + return nil, err + } + } + + return &gctrpc.GenericResponse{Status: MsgStatusSuccess}, nil +} + +// UpdateExchangeSupportedPairs forces an update of the supported pairs which +// will update the available pairs list and remove any assets that are disabled +// by the exchange +func (s *RPCServer) UpdateExchangeSupportedPairs(_ context.Context, r *gctrpc.UpdateExchangeSupportedPairsRequest) (*gctrpc.GenericResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + base := exch.GetBase() + if base == nil { + return nil, errExchangeBaseNotFound + } + + if !base.GetEnabledFeatures().AutoPairUpdates { + return nil, + errors.New("cannot auto pair update for exchange, a manual update is needed") + } + + err := exch.UpdateTradablePairs(false) + if err != nil { + return nil, err + } + + if exch.IsWebsocketEnabled() { + err = exch.FlushWebsocketChannels() + if err != nil { + return nil, err + } + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess}, nil +} + +// GetExchangeAssets returns the supported asset types +func (s *RPCServer) GetExchangeAssets(_ context.Context, r *gctrpc.GetExchangeAssetsRequest) (*gctrpc.GetExchangeAssetsResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + return &gctrpc.GetExchangeAssetsResponse{ + Assets: exch.GetAssetTypes().JoinToString(","), + }, nil +} + +// WebsocketGetInfo returns websocket connection information +func (s *RPCServer) WebsocketGetInfo(_ context.Context, r *gctrpc.WebsocketGetInfoRequest) (*gctrpc.WebsocketGetInfoResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + w, err := exch.GetWebsocket() + if err != nil { + return nil, err + } + + return &gctrpc.WebsocketGetInfoResponse{ + Exchange: exch.GetName(), + Supported: exch.SupportsWebsocket(), + Enabled: exch.IsWebsocketEnabled(), + Authenticated: w.CanUseAuthenticatedEndpoints(), + RunningUrl: w.GetWebsocketURL(), + ProxyAddress: w.GetProxyAddress(), + }, nil +} + +// WebsocketSetEnabled enables or disables the websocket client +func (s *RPCServer) WebsocketSetEnabled(_ context.Context, r *gctrpc.WebsocketSetEnabledRequest) (*gctrpc.GenericResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + w, err := exch.GetWebsocket() + if err != nil { + return nil, fmt.Errorf("websocket not supported for exchange %s", r.Exchange) + } + + exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange) + if err != nil { + return nil, err + } + + if r.Enable { + err = w.Enable() + if err != nil { + return nil, err + } + + exchCfg.Features.Enabled.Websocket = true + return &gctrpc.GenericResponse{Status: MsgStatusSuccess, Data: "websocket enabled"}, nil + } + + err = w.Disable() + if err != nil { + return nil, err + } + exchCfg.Features.Enabled.Websocket = false + return &gctrpc.GenericResponse{Status: MsgStatusSuccess, Data: "websocket disabled"}, nil +} + +// WebsocketGetSubscriptions returns websocket subscription analysis +func (s *RPCServer) WebsocketGetSubscriptions(_ context.Context, r *gctrpc.WebsocketGetSubscriptionsRequest) (*gctrpc.WebsocketGetSubscriptionsResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + w, err := exch.GetWebsocket() + if err != nil { + return nil, fmt.Errorf("websocket not supported for exchange %s", r.Exchange) + } + + payload := new(gctrpc.WebsocketGetSubscriptionsResponse) + payload.Exchange = exch.GetName() + subs := w.GetSubscriptions() + for i := range subs { + params, err := json.Marshal(subs[i].Params) + if err != nil { + return nil, err + } + payload.Subscriptions = append(payload.Subscriptions, + &gctrpc.WebsocketSubscription{ + Channel: subs[i].Channel, + Currency: subs[i].Currency.String(), + Asset: subs[i].Asset.String(), + Params: string(params), + }) + } + return payload, nil +} + +// WebsocketSetProxy sets client websocket connection proxy +func (s *RPCServer) WebsocketSetProxy(_ context.Context, r *gctrpc.WebsocketSetProxyRequest) (*gctrpc.GenericResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + w, err := exch.GetWebsocket() + if err != nil { + return nil, fmt.Errorf("websocket not supported for exchange %s", r.Exchange) + } + + err = w.SetProxyAddress(r.Proxy) + if err != nil { + return nil, err + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess, + Data: fmt.Sprintf("new proxy has been set [%s] for %s websocket connection", + r.Exchange, + r.Proxy)}, nil +} + +// WebsocketSetURL sets exchange websocket client connection URL +func (s *RPCServer) WebsocketSetURL(_ context.Context, r *gctrpc.WebsocketSetURLRequest) (*gctrpc.GenericResponse, error) { + exch := GetExchangeByName(r.Exchange) + if exch == nil { + return nil, errExchangeNotLoaded + } + + w, err := exch.GetWebsocket() + if err != nil { + return nil, fmt.Errorf("websocket not supported for exchange %s", r.Exchange) + } + + err = w.SetWebsocketURL(r.Url, false, true) + if err != nil { + return nil, err + } + return &gctrpc.GenericResponse{Status: MsgStatusSuccess, + Data: fmt.Sprintf("new URL has been set [%s] for %s websocket connection", + r.Exchange, + r.Url)}, nil } diff --git a/engine/syncer.go b/engine/syncer.go index a634da41..bf4f8e8d 100644 --- a/engine/syncer.go +++ b/engine/syncer.go @@ -200,16 +200,18 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair } switch syncType { - case SyncItemOrderbook, SyncItemTrade, SyncItemTicker: - if !e.Cfg.SyncOrderbook && syncType == SyncItemOrderbook { + case SyncItemOrderbook: + if !e.Cfg.SyncOrderbook { return } - if !e.Cfg.SyncTicker && syncType == SyncItemTicker { + case SyncItemTicker: + if !e.Cfg.SyncTicker { return } - if !e.Cfg.SyncTrades && syncType == SyncItemTrade { + case SyncItemTrade: + if !e.Cfg.SyncTrades { return } default: @@ -236,7 +238,10 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair if atomic.LoadInt32(&e.initSyncCompleted) != 1 && !origHadData { removedCounter++ log.Debugf(log.SyncMgr, "%s ticker sync complete %v [%d/%d].\n", - exchangeName, FormatCurrency(p).String(), removedCounter, createdCounter) + exchangeName, + FormatCurrency(p).String(), + removedCounter, + createdCounter) e.initSyncWG.Done() } @@ -251,7 +256,10 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair if atomic.LoadInt32(&e.initSyncCompleted) != 1 && !origHadData { removedCounter++ log.Debugf(log.SyncMgr, "%s orderbook sync complete %v [%d/%d].\n", - exchangeName, FormatCurrency(p).String(), removedCounter, createdCounter) + exchangeName, + FormatCurrency(p).String(), + removedCounter, + createdCounter) e.initSyncWG.Done() } @@ -266,7 +274,10 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair if atomic.LoadInt32(&e.initSyncCompleted) != 1 && !origHadData { removedCounter++ log.Debugf(log.SyncMgr, "%s trade sync complete %v [%d/%d].\n", - exchangeName, FormatCurrency(p).String(), removedCounter, createdCounter) + exchangeName, + FormatCurrency(p).String(), + removedCounter, + createdCounter) e.initSyncWG.Done() } } @@ -276,7 +287,8 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair func (e *ExchangeCurrencyPairSyncer) worker() { cleanup := func() { - log.Debugln(log.SyncMgr, "Exchange CurrencyPairSyncer worker shutting down.") + log.Debugln(log.SyncMgr, + "Exchange CurrencyPairSyncer worker shutting down.") } defer cleanup() @@ -293,8 +305,10 @@ func (e *ExchangeCurrencyPairSyncer) worker() { if exchanges[x].SupportsWebsocket() && exchanges[x].IsWebsocketEnabled() { ws, err := exchanges[x].GetWebsocket() if err != nil { - log.Errorf(log.SyncMgr, "%s unable to get websocket pointer. Err: %s\n", - exchangeName, err) + log.Errorf(log.SyncMgr, + "%s unable to get websocket pointer. Err: %s\n", + exchangeName, + err) usingREST = true } @@ -308,7 +322,17 @@ func (e *ExchangeCurrencyPairSyncer) worker() { } for y := range assetTypes { - enabledPairs := exchanges[x].GetEnabledPairs(assetTypes[y]) + if exchanges[x].GetBase().CurrencyPairs.IsAssetEnabled(assetTypes[y]) != nil { + continue + } + enabledPairs, err := exchanges[x].GetEnabledPairs(assetTypes[y]) + if err != nil { + log.Errorf(log.SyncMgr, + "%s failed to get enabled pairs. Err: %s\n", + exchangeName, + err) + continue + } for i := range enabledPairs { if atomic.LoadInt32(&e.shutdown) == 1 { return @@ -410,7 +434,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() { } else { result, err = exchanges[x].UpdateTicker(c.Pair, c.AssetType) } - printTickerSummary(result, c.Pair, c.AssetType, exchangeName, "REST", err) + printTickerSummary(result, "REST", err) if err == nil { if Bot.Config.RemoteControl.WebsocketRPC.Enabled { relayWebsocketEvent(result, "ticker_update", c.AssetType.String(), exchangeName) @@ -449,7 +473,7 @@ func (e *ExchangeCurrencyPairSyncer) worker() { e.setProcessing(c.Exchange, c.Pair, c.AssetType, SyncItemOrderbook, true) result, err := exchanges[x].UpdateOrderbook(c.Pair, c.AssetType) - printOrderbookSummary(result, c.Pair, c.AssetType, exchangeName, "REST", err) + printOrderbookSummary(result, "REST", err) if err == nil { if Bot.Config.RemoteControl.WebsocketRPC.Enabled { relayWebsocketEvent(result, "orderbook_update", c.AssetType.String(), exchangeName) @@ -497,8 +521,10 @@ func (e *ExchangeCurrencyPairSyncer) Start() { if supportsWebsocket && exchanges[x].IsWebsocketEnabled() { ws, err := exchanges[x].GetWebsocket() if err != nil { - log.Errorf(log.SyncMgr, "%s failed to get websocket. Err: %s\n", - exchangeName, err) + log.Errorf(log.SyncMgr, + "%s failed to get websocket. Err: %s\n", + exchangeName, + err) usingREST = true } @@ -507,8 +533,10 @@ func (e *ExchangeCurrencyPairSyncer) Start() { err = ws.Connect() if err != nil { - log.Errorf(log.SyncMgr, "%s websocket failed to connect. Err: %s\n", - exchangeName, err) + log.Errorf(log.SyncMgr, + "%s websocket failed to connect. Err: %s\n", + exchangeName, + err) usingREST = true } else { usingWebsocket = true @@ -521,7 +549,22 @@ func (e *ExchangeCurrencyPairSyncer) Start() { } for y := range assetTypes { - enabledPairs := exchanges[x].GetEnabledPairs(assetTypes[y]) + if exchanges[x].GetBase().CurrencyPairs.IsAssetEnabled(assetTypes[y]) != nil { + log.Warnf(log.SyncMgr, + "%s asset type %s is disabled, fetching enabled pairs is paused", + exchangeName, + assetTypes[y]) + continue + } + + enabledPairs, err := exchanges[x].GetEnabledPairs(assetTypes[y]) + if err != nil { + log.Errorf(log.SyncMgr, + "%s failed to get enabled pairs. Err: %s\n", + exchangeName, + err) + continue + } for i := range enabledPairs { if e.exists(exchangeName, enabledPairs[i], assetTypes[y]) { continue diff --git a/engine/websocket.go b/engine/websocket.go index e277e788..99cef467 100644 --- a/engine/websocket.go +++ b/engine/websocket.go @@ -351,8 +351,14 @@ func wsGetTicker(client *WebsocketClient, data interface{}) error { return err } - result, err := GetSpecificTicker(currency.NewPairFromString(tickerReq.Currency), - tickerReq.Exchange, asset.Item(tickerReq.AssetType)) + p, err := currency.NewPairFromString(tickerReq.Currency) + if err != nil { + return err + } + + result, err := GetSpecificTicker(p, + tickerReq.Exchange, + asset.Item(tickerReq.AssetType)) if err != nil { wsResp.Error = err.Error() @@ -383,7 +389,12 @@ func wsGetOrderbook(client *WebsocketClient, data interface{}) error { return err } - result, err := GetSpecificOrderbook(currency.NewPairFromString(orderbookReq.Currency), + p, err := currency.NewPairFromString(orderbookReq.Currency) + if err != nil { + return err + } + + result, err := GetSpecificOrderbook(p, orderbookReq.Exchange, asset.Item(orderbookReq.AssetType)) if err != nil { diff --git a/exchanges/alphapoint/alphapoint_websocket.go b/exchanges/alphapoint/alphapoint_websocket.go index dfb65979..54a9c913 100644 --- a/exchanges/alphapoint/alphapoint_websocket.go +++ b/exchanges/alphapoint/alphapoint_websocket.go @@ -40,7 +40,6 @@ func (a *Alphapoint) WebsocketClient() { for a.Enabled { msgType, resp, err := a.WebsocketConn.ReadMessage() if err != nil { - a.Websocket.ReadMessageErrors <- err log.Error(log.ExchangeSys, err) break } diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index 0eb6fe15..bb2eaeb7 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -16,7 +16,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -35,10 +34,6 @@ func (a *Alphapoint) SetDefaults() { a.API.CredentialsValidator.RequiresKey = true a.API.CredentialsValidator.RequiresSecret = true - a.CurrencyPairs.AssetTypes = asset.Items{ - asset.Spot, - } - a.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ REST: true, @@ -129,23 +124,24 @@ func (a *Alphapoint) FetchAccountInfo() (account.Holdings, error) { // UpdateTicker updates and returns the ticker for a currency pair func (a *Alphapoint) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) tick, err := a.GetTicker(p.String()) if err != nil { - return tickerPrice, err + return nil, err } - tickerPrice.Pair = p - tickerPrice.Ask = tick.Ask - tickerPrice.Bid = tick.Bid - tickerPrice.Low = tick.Low - tickerPrice.High = tick.High - tickerPrice.Volume = tick.Volume - tickerPrice.Last = tick.Last - - err = ticker.ProcessTicker(a.Name, tickerPrice, assetType) + err = ticker.ProcessTicker(&ticker.Price{ + Pair: p, + Ask: tick.Ask, + Bid: tick.Bid, + Low: tick.Low, + High: tick.High, + Volume: tick.Volume, + Last: tick.Last, + ExchangeName: a.Name, + AssetType: assetType, + }) if err != nil { - return tickerPrice, err + return nil, err } return ticker.GetTicker(a.Name, p, assetType) @@ -313,11 +309,6 @@ func (a *Alphapoint) WithdrawFiatFundsToInternationalBank(withdrawRequest *withd return "", common.ErrNotYetImplemented } -// GetWebsocket returns a pointer to the exchange websocket -func (a *Alphapoint) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrNotYetImplemented -} - // GetFeeByType returns an estimate of fee based on type of transaction func (a *Alphapoint) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { return 0, common.ErrFunctionNotSupported @@ -406,28 +397,6 @@ func (a *Alphapoint) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detai return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (a *Alphapoint) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (a *Alphapoint) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (a *Alphapoint) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (a *Alphapoint) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (a *Alphapoint) ValidateCredentials() error { diff --git a/exchanges/asset/asset.go b/exchanges/asset/asset.go index 43947812..5e80d0f7 100644 --- a/exchanges/asset/asset.go +++ b/exchanges/asset/asset.go @@ -14,6 +14,7 @@ type Items []Item const ( Spot = Item("spot") Margin = Item("margin") + MarginFunding = Item("marginfunding") Index = Item("index") Binary = Item("binary") PerpetualContract = Item("perpetualcontract") @@ -26,6 +27,7 @@ const ( var supported = Items{ Spot, Margin, + MarginFunding, Index, Binary, PerpetualContract, @@ -66,7 +68,6 @@ func (a Items) Contains(i Item) bool { return true } } - return false } diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index 66fcf280..33987279 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -17,9 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -63,7 +61,6 @@ const ( // Binance is the overarching type across the Bithumb package type Binance struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection // Valid string list that is required by the exchange validLimits []int @@ -569,17 +566,6 @@ func (b *Binance) CheckLimit(limit int) error { return errors.New("incorrect limit values - valid values are 5, 10, 20, 50, 100, 500, 1000") } -// CheckSymbol checks value against a variable list -func (b *Binance) CheckSymbol(symbol string, assetType asset.Item) error { - enPairs := b.GetAvailablePairs(assetType) - for x := range enPairs { - if b.FormatExchangeCurrency(enPairs[x], assetType).String() == symbol { - return nil - } - } - return errors.New("incorrect symbol values - please check available pairs in configuration") -} - // SetValues sets the default valid values func (b *Binance) SetValues() { b.validLimits = []int{5, 10, 20, 50, 100, 500, 1000, 5000} diff --git a/exchanges/binance/binance_live_test.go b/exchanges/binance/binance_live_test.go index 643af815..b20f29e4 100644 --- a/exchanges/binance/binance_live_test.go +++ b/exchanges/binance/binance_live_test.go @@ -29,6 +29,7 @@ func TestMain(m *testing.M) { binanceConfig.API.Credentials.Key = apiKey binanceConfig.API.Credentials.Secret = apiSecret b.SetDefaults() + b.Websocket = sharedtestvalues.NewTestWebsocket() err = b.Setup(binanceConfig) if err != nil { log.Fatal("Binance setup error", err) diff --git a/exchanges/binance/binance_mock_test.go b/exchanges/binance/binance_mock_test.go index 55974656..0bec281b 100644 --- a/exchanges/binance/binance_mock_test.go +++ b/exchanges/binance/binance_mock_test.go @@ -33,6 +33,7 @@ func TestMain(m *testing.M) { binanceConfig.API.Credentials.Key = apiKey binanceConfig.API.Credentials.Secret = apiSecret b.SetDefaults() + b.Websocket = sharedtestvalues.NewTestWebsocket() err = b.Setup(binanceConfig) if err != nil { log.Fatal("Binance setup error", err) @@ -45,7 +46,6 @@ func TestMain(m *testing.M) { b.HTTPClient = newClient b.API.Endpoints.URL = serverDetails - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() log.Printf(sharedtestvalues.MockTesting, b.Name, b.API.Endpoints.URL) os.Exit(m.Run()) } diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index bfd2fb69..6fa58ab7 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -206,7 +206,7 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { var feeBuilder = setFeeBuilder() b.GetFeeByType(feeBuilder) - if !areTestAPIKeysSet() { + if !areTestAPIKeysSet() || mockTests { if feeBuilder.FeeType != exchange.OfflineTradeFee { t.Errorf("Expected %v, received %v", exchange.OfflineTradeFee, feeBuilder.FeeType) } @@ -439,6 +439,7 @@ func TestCancelExchangeOrder(t *testing.T) { WalletAddress: core.BitcoinDonationAddress, AccountID: "1", Pair: currency.NewPair(currency.LTC, currency.BTC), + AssetType: asset.Spot, } err := b.CancelOrder(orderCancellation) @@ -904,10 +905,13 @@ func TestExecutionTypeToOrderStatus(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSDT") + currencyPair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } startTime := time.Unix(1546300800, 0) end := time.Unix(1577836799, 0) - _, err := b.GetHistoricCandles(currencyPair, asset.Spot, startTime, end, kline.OneDay) + _, err = b.GetHistoricCandles(currencyPair, asset.Spot, startTime, end, kline.OneDay) if err != nil { t.Fatal(err) } @@ -919,10 +923,13 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSDT") + currencyPair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } startTime := time.Unix(1546300800, 0) end := time.Unix(1577836799, 0) - _, err := b.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, end, kline.OneDay) + _, err = b.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, end, kline.OneDay) if err != nil { t.Fatal(err) } diff --git a/exchanges/binance/binance_types.go b/exchanges/binance/binance_types.go index 9a9aff26..b3516e33 100644 --- a/exchanges/binance/binance_types.go +++ b/exchanges/binance/binance_types.go @@ -25,15 +25,19 @@ type ExchangeInfo struct { } `json:"rateLimits"` ExchangeFilters interface{} `json:"exchangeFilters"` Symbols []struct { - Symbol string `json:"symbol"` - Status string `json:"status"` - BaseAsset string `json:"baseAsset"` - BaseAssetPrecision int `json:"baseAssetPrecision"` - QuoteAsset string `json:"quoteAsset"` - QuotePrecision int `json:"quotePrecision"` - OrderTypes []string `json:"orderTypes"` - IcebergAllowed bool `json:"icebergAllowed"` - Filters []struct { + Symbol string `json:"symbol"` + Status string `json:"status"` + BaseAsset string `json:"baseAsset"` + BaseAssetPrecision int `json:"baseAssetPrecision"` + QuoteAsset string `json:"quoteAsset"` + QuotePrecision int `json:"quotePrecision"` + OrderTypes []string `json:"orderTypes"` + IcebergAllowed bool `json:"icebergAllowed"` + OCOAllowed bool `json:"ocoAllowed"` + QuoteOrderQtyMarketAllowed bool `json:"quoteOrderQtyMarketAllowed"` + IsSpotTradingAllowed bool `json:"isSpotTradingAllowed"` + IsMarginTradingAllowed bool `json:"isMarginTradingAllowed"` + Filters []struct { FilterType string `json:"filterType"` MinPrice float64 `json:"minPrice,string"` MaxPrice float64 `json:"maxPrice,string"` @@ -594,89 +598,111 @@ type UserAccountStream struct { } type wsAccountInfo struct { - CanDeposit bool `json:"D"` - CanTrade bool `json:"T"` - CanWithdraw bool `json:"W"` - EventTime int64 `json:"E"` - LastUpdated int64 `json:"u"` - BuyerCommission float64 `json:"b"` - MakerCommission float64 `json:"m"` - SellerCommission float64 `json:"s"` - TakerCommission float64 `json:"t"` - EventType string `json:"e"` - Currencies []struct { - Asset string `json:"a"` - Available float64 `json:"f,string"` - Locked float64 `json:"l,string"` - } `json:"B"` + Stream string `json:"stream"` + Data struct { + CanDeposit bool `json:"D"` + CanTrade bool `json:"T"` + CanWithdraw bool `json:"W"` + EventTime int64 `json:"E"` + LastUpdated int64 `json:"u"` + BuyerCommission float64 `json:"b"` + MakerCommission float64 `json:"m"` + SellerCommission float64 `json:"s"` + TakerCommission float64 `json:"t"` + EventType string `json:"e"` + Currencies []struct { + Asset string `json:"a"` + Available float64 `json:"f,string"` + Locked float64 `json:"l,string"` + } `json:"B"` + } `json:"data"` } type wsAccountPosition struct { - Currencies []struct { - Asset string `json:"a"` - Available float64 `json:"f,string"` - Locked float64 `json:"l,string"` - } `json:"B"` - EventTime int64 `json:"E"` - LastUpdated int64 `json:"u"` - EventType string `json:"e"` + Stream string `json:"stream"` + Data struct { + Currencies []struct { + Asset string `json:"a"` + Available float64 `json:"f,string"` + Locked float64 `json:"l,string"` + } `json:"B"` + EventTime int64 `json:"E"` + LastUpdated int64 `json:"u"` + EventType string `json:"e"` + } `json:"data"` } type wsBalanceUpdate struct { - EventTime int64 `json:"E"` - ClearTime int64 `json:"T"` - BalanceDelta float64 `json:"d,string"` - Asset string `json:"a"` - EventType string `json:"e"` + Stream string `json:"stream"` + Data struct { + EventTime int64 `json:"E"` + ClearTime int64 `json:"T"` + BalanceDelta float64 `json:"d,string"` + Asset string `json:"a"` + EventType string `json:"e"` + } `json:"data"` } type wsOrderUpdate struct { - ClientOrderID string `json:"C"` - EventTime int64 `json:"E"` - IcebergQuantity float64 `json:"F,string"` - LastExecutedPrice float64 `json:"L,string"` - CommissionAsset float64 `json:"N"` - OrderCreationTime int64 `json:"O"` - StopPrice float64 `json:"P,string"` - QuoteOrderQuantity float64 `json:"Q,string"` - Side string `json:"S"` - TransactionTime int64 `json:"T"` - OrderStatus string `json:"X"` - LastQuoteAssetTransactedQuantity float64 `json:"Y,string"` - CumulativeQuoteTransactedQuantity float64 `json:"Z,string"` - CancelledClientOrderID string `json:"c"` - EventType string `json:"e"` - TimeInForce string `json:"f"` - OrderListID int64 `json:"g"` - OrderID int64 `json:"i"` - LastExecutedQuantity float64 `json:"l,string"` - IsMaker bool `json:"m"` - Commission float64 `json:"n,string"` - OrderType string `json:"o"` - Price float64 `json:"p,string"` - Quantity float64 `json:"q,string"` - RejectionReason string `json:"r"` - Symbol string `json:"s"` - TradeID int64 `json:"t"` - IsOnOrderBook bool `json:"w"` - CurrentExecutionType string `json:"x"` - CumulativeFilledQuantity float64 `json:"z,string"` + Stream string `json:"stream"` + Data struct { + ClientOrderID string `json:"C"` + EventTime int64 `json:"E"` + IcebergQuantity float64 `json:"F,string"` + LastExecutedPrice float64 `json:"L,string"` + CommissionAsset float64 `json:"N"` + OrderCreationTime int64 `json:"O"` + StopPrice float64 `json:"P,string"` + QuoteOrderQuantity float64 `json:"Q,string"` + Side string `json:"S"` + TransactionTime int64 `json:"T"` + OrderStatus string `json:"X"` + LastQuoteAssetTransactedQuantity float64 `json:"Y,string"` + CumulativeQuoteTransactedQuantity float64 `json:"Z,string"` + CancelledClientOrderID string `json:"c"` + EventType string `json:"e"` + TimeInForce string `json:"f"` + OrderListID int64 `json:"g"` + OrderID int64 `json:"i"` + LastExecutedQuantity float64 `json:"l,string"` + IsMaker bool `json:"m"` + Commission float64 `json:"n,string"` + OrderType string `json:"o"` + Price float64 `json:"p,string"` + Quantity float64 `json:"q,string"` + RejectionReason string `json:"r"` + Symbol string `json:"s"` + TradeID int64 `json:"t"` + IsOnOrderBook bool `json:"w"` + CurrentExecutionType string `json:"x"` + CumulativeFilledQuantity float64 `json:"z,string"` + } `json:"data"` } -type wsListStauts struct { - ListClientOrderID string `json:"C"` - EventTime int64 `json:"E"` - ListOrderStatus string `json:"L"` - Orders []struct { - ClientOrderID string `json:"c"` - OrderID int64 `json:"i"` - Symbol string `json:"s"` - } `json:"O"` - TransactionTime int64 `json:"T"` - ContingencyType string `json:"c"` - EventType string `json:"e"` - OrderListID int64 `json:"g"` - ListStatusType string `json:"l"` - RejectionReason string `json:"r"` - Symbol string `json:"s"` +type wsListStatus struct { + Stream string `json:"stream"` + Data struct { + ListClientOrderID string `json:"C"` + EventTime int64 `json:"E"` + ListOrderStatus string `json:"L"` + Orders []struct { + ClientOrderID string `json:"c"` + OrderID int64 `json:"i"` + Symbol string `json:"s"` + } `json:"O"` + TransactionTime int64 `json:"T"` + ContingencyType string `json:"c"` + EventType string `json:"e"` + OrderListID int64 `json:"g"` + ListStatusType string `json:"l"` + RejectionReason string `json:"r"` + Symbol string `json:"s"` + } `json:"data"` +} + +// WsPayload defines the payload through the websocket connection +type WsPayload struct { + Method string `json:"method"` + Params []string `json:"params"` + ID int64 `json:"id"` } diff --git a/exchanges/binance/binance_websocket.go b/exchanges/binance/binance_websocket.go index 260af14d..2b58b288 100644 --- a/exchanges/binance/binance_websocket.go +++ b/exchanges/binance/binance_websocket.go @@ -14,23 +14,23 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" "github.com/thrasher-corp/gocryptotrader/log" ) const ( - binanceDefaultWebsocketURL = "wss://stream.binance.com:9443" + binanceDefaultWebsocketURL = "wss://stream.binance.com:9443/stream" pingDelay = time.Minute * 9 ) var listenKey string -// WsConnect intiates a websocket connection +// WsConnect initiates a websocket connection func (b *Binance) WsConnect() error { if !b.Websocket.IsEnabled() || !b.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer @@ -39,54 +39,43 @@ func (b *Binance) WsConnect() error { listenKey, err = b.GetWsAuthStreamKey() if err != nil { b.Websocket.SetCanUseAuthenticatedEndpoints(false) - log.Errorf(log.ExchangeSys, "%v unable to connect to authenticated Websocket. Error: %s", b.Name, err) + log.Errorf(log.ExchangeSys, + "%v unable to connect to authenticated Websocket. Error: %s", + b.Name, + err) + } else { + // cleans on failed connection + clean := strings.Split(b.Websocket.GetWebsocketURL(), "?streams=") + authPayload := clean[0] + "?streams=" + listenKey + err = b.Websocket.SetWebsocketURL(authPayload, false, false) + if err != nil { + return err + } } } - pairs := b.GetEnabledPairs(asset.Spot).Strings() - tick := strings.ToLower( - strings.Replace( - strings.Join(pairs, "@ticker/"), "-", "", -1)) + "@ticker" - trade := strings.ToLower( - strings.Replace( - strings.Join(pairs, "@trade/"), "-", "", -1)) + "@trade" - kline := strings.ToLower( - strings.Replace( - strings.Join(pairs, "@kline_1m/"), "-", "", -1)) + "@kline_1m" - depth := strings.ToLower( - strings.Replace( - strings.Join(pairs, "@depth/"), "-", "", -1)) + "@depth" - - wsurl := b.Websocket.GetWebsocketURL() + - "/stream?streams=" + - tick + - "/" + - trade + - "/" + - kline + - "/" + - depth - if listenKey != "" { - wsurl += "/" + - listenKey - } - - b.WebsocketConn.URL = wsurl - b.WebsocketConn.Verbose = b.Verbose - - err = b.WebsocketConn.Dial(&dialer, http.Header{}) + err = b.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return fmt.Errorf("%v - Unable to connect to Websocket. Error: %s", b.Name, err) } - b.WebsocketConn.SetupPingHandler(wshandler.WebsocketPingHandler{ + + if b.Websocket.CanUseAuthenticatedEndpoints() { + go b.KeepAuthKeyAlive() + } + + b.Websocket.Conn.SetupPingHandler(stream.PingHandler{ UseGorillaHandler: true, MessageType: websocket.PongMessage, Delay: pingDelay, }) - enabledPairs := b.GetEnabledPairs(asset.Spot) + enabledPairs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + for i := range enabledPairs { err = b.SeedLocalCache(enabledPairs[i]) if err != nil { @@ -95,17 +84,19 @@ func (b *Binance) WsConnect() error { } go b.wsReadData() - go b.KeepAuthKeyAlive() - return nil + + subs, err := b.GenerateSubscriptions() + if err != nil { + return err + } + return b.Websocket.SubscribeToChannels(subs) } // KeepAuthKeyAlive will continuously send messages to // keep the WS auth key active func (b *Binance) KeepAuthKeyAlive() { b.Websocket.Wg.Add(1) - defer func() { - b.Websocket.Wg.Done() - }() + defer b.Websocket.Wg.Done() ticks := time.NewTicker(time.Minute * 30) for { select { @@ -116,7 +107,8 @@ func (b *Binance) KeepAuthKeyAlive() { err := b.MaintainWsAuthStreamKey() if err != nil { b.Websocket.DataHandler <- err - log.Warnf(log.ExchangeSys, b.Name+" - Unable to renew auth websocket token, may experience shutdown") + log.Warnf(log.ExchangeSys, + b.Name+" - Unable to renew auth websocket token, may experience shutdown") } } } @@ -125,25 +117,16 @@ func (b *Binance) KeepAuthKeyAlive() { // wsReadData receives and passes on websocket messages for processing func (b *Binance) wsReadData() { b.Websocket.Wg.Add(1) - defer func() { - b.Websocket.Wg.Done() - }() - for { - select { - case <-b.Websocket.ShutdownC: - return + defer b.Websocket.Wg.Done() - default: - resp, err := b.WebsocketConn.ReadMessage() - if err != nil { - b.Websocket.ReadMessageErrors <- err - return - } - b.Websocket.TrafficAlert <- struct{}{} - err = b.wsHandleData(resp.Raw) - if err != nil { - b.Websocket.DataHandler <- err - } + for { + resp := b.Websocket.Conn.ReadMessage() + if resp.Raw == nil { + return + } + err := b.wsHandleData(resp.Raw) + if err != nil { + b.Websocket.DataHandler <- err } } } @@ -163,112 +146,122 @@ func (b *Binance) wsHandleData(respRaw []byte) error { return nil } } - if e, ok := multiStreamData["e"].(string); ok { - switch e { - case "outboundAccountInfo": - var data wsAccountInfo - err := json.Unmarshal(respRaw, &data) - if err != nil { - return fmt.Errorf("%v - Could not convert to outboundAccountInfo structure %s", - b.Name, - err) - } - b.Websocket.DataHandler <- data - case "outboundAccountPosition": - var data wsAccountPosition - err := json.Unmarshal(respRaw, &data) - if err != nil { - return fmt.Errorf("%v - Could not convert to outboundAccountPosition structure %s", - b.Name, - err) - } - b.Websocket.DataHandler <- data - case "balanceUpdate": - var data wsBalanceUpdate - err := json.Unmarshal(respRaw, &data) - if err != nil { - return fmt.Errorf("%v - Could not convert to balanceUpdate structure %s", - b.Name, - err) - } - b.Websocket.DataHandler <- data - case "executionReport": - var data wsOrderUpdate - err := json.Unmarshal(respRaw, &data) - if err != nil { - return fmt.Errorf("%v - Could not convert to executionReport structure %s", - b.Name, - err) - } - var orderID = strconv.FormatInt(data.OrderID, 10) - oType, err := order.StringToOrderType(data.OrderType) - if err != nil { - b.Websocket.DataHandler <- order.ClassificationError{ - Exchange: b.Name, - OrderID: orderID, - Err: err, + if newdata, ok := multiStreamData["data"].(map[string]interface{}); ok { + if e, ok := newdata["e"].(string); ok { + switch e { + case "outboundAccountInfo": + var data wsAccountInfo + err := json.Unmarshal(respRaw, &data) + if err != nil { + return fmt.Errorf("%v - Could not convert to outboundAccountInfo structure %s", + b.Name, + err) } - } - var oSide order.Side - oSide, err = order.StringToOrderSide(data.Side) - if err != nil { - b.Websocket.DataHandler <- order.ClassificationError{ - Exchange: b.Name, - OrderID: orderID, - Err: err, + b.Websocket.DataHandler <- data + case "outboundAccountPosition": + var data wsAccountPosition + err := json.Unmarshal(respRaw, &data) + if err != nil { + return fmt.Errorf("%v - Could not convert to outboundAccountPosition structure %s", + b.Name, + err) } - } - var oStatus order.Status - oStatus, err = stringToOrderStatus(data.CurrentExecutionType) - if err != nil { - b.Websocket.DataHandler <- order.ClassificationError{ - Exchange: b.Name, - OrderID: orderID, - Err: err, + b.Websocket.DataHandler <- data + case "balanceUpdate": + var data wsBalanceUpdate + err := json.Unmarshal(respRaw, &data) + if err != nil { + return fmt.Errorf("%v - Could not convert to balanceUpdate structure %s", + b.Name, + err) } + b.Websocket.DataHandler <- data + case "executionReport": + var data wsOrderUpdate + err := json.Unmarshal(respRaw, &data) + if err != nil { + return fmt.Errorf("%v - Could not convert to executionReport structure %s", + b.Name, + err) + } + var orderID = strconv.FormatInt(data.Data.OrderID, 10) + oType, err := order.StringToOrderType(data.Data.OrderType) + if err != nil { + b.Websocket.DataHandler <- order.ClassificationError{ + Exchange: b.Name, + OrderID: orderID, + Err: err, + } + } + var oSide order.Side + oSide, err = order.StringToOrderSide(data.Data.Side) + if err != nil { + b.Websocket.DataHandler <- order.ClassificationError{ + Exchange: b.Name, + OrderID: orderID, + Err: err, + } + } + var oStatus order.Status + oStatus, err = stringToOrderStatus(data.Data.CurrentExecutionType) + if err != nil { + b.Websocket.DataHandler <- order.ClassificationError{ + Exchange: b.Name, + OrderID: orderID, + Err: err, + } + } + var p currency.Pair + var a asset.Item + p, a, err = b.GetRequestFormattedPairAndAssetType(data.Data.Symbol) + if err != nil { + return err + } + b.Websocket.DataHandler <- &order.Detail{ + Price: data.Data.Price, + Amount: data.Data.Quantity, + ExecutedAmount: data.Data.CumulativeFilledQuantity, + RemainingAmount: data.Data.Quantity - data.Data.CumulativeFilledQuantity, + Exchange: b.Name, + ID: orderID, + Type: oType, + Side: oSide, + Status: oStatus, + AssetType: a, + Date: time.Unix(0, data.Data.OrderCreationTime*int64(time.Millisecond)), + Pair: p, + } + case "listStatus": + var data wsListStatus + err := json.Unmarshal(respRaw, &data) + if err != nil { + return fmt.Errorf("%v - Could not convert to listStatus structure %s", + b.Name, + err) + } + b.Websocket.DataHandler <- data } - var p currency.Pair - var a asset.Item - p, a, err = b.GetRequestFormattedPairAndAssetType(data.Symbol) - if err != nil { - return err - } - b.Websocket.DataHandler <- &order.Detail{ - Price: data.Price, - Amount: data.Quantity, - ExecutedAmount: data.CumulativeFilledQuantity, - RemainingAmount: data.Quantity - data.CumulativeFilledQuantity, - Exchange: b.Name, - ID: orderID, - Type: oType, - Side: oSide, - Status: oStatus, - AssetType: a, - Date: time.Unix(0, data.OrderCreationTime*int64(time.Millisecond)), - Pair: p, - } - case "listStatus": - var data wsListStauts - err := json.Unmarshal(respRaw, &data) - if err != nil { - return fmt.Errorf("%v - Could not convert to listStatus structure %s", - b.Name, - err) - } - b.Websocket.DataHandler <- data - default: - b.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: b.Name + wshandler.UnhandledMessage + string(respRaw)} - return nil } } - if stream, ok := multiStreamData["stream"].(string); ok { - streamType := strings.Split(stream, "@") + if wsStream, ok := multiStreamData["stream"].(string); ok { + streamType := strings.Split(wsStream, "@") if len(streamType) > 1 { if data, ok := multiStreamData["data"]; ok { rawData, err := json.Marshal(data) if err != nil { return err } + + pairs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + + format, err := b.GetPairFormat(asset.Spot, true) + if err != nil { + return err + } + switch streamType[1] { case "trade": var trade TradeStream @@ -293,14 +286,18 @@ func (b *Binance) wsHandleData(respRaw []byte) error { err) } - b.Websocket.DataHandler <- wshandler.TradeData{ - CurrencyPair: currency.NewPairFromFormattedPairs(trade.Symbol, b.GetEnabledPairs(asset.Spot), - b.GetPairFormat(asset.Spot, true)), - Timestamp: time.Unix(0, trade.TimeStamp*int64(time.Millisecond)), - Price: price, - Amount: amount, - Exchange: b.Name, - AssetType: asset.Spot, + pair, err := currency.NewPairFromFormattedPairs(trade.Symbol, pairs, format) + if err != nil { + return err + } + + b.Websocket.DataHandler <- stream.TradeData{ + CurrencyPair: pair, + Timestamp: time.Unix(0, trade.TimeStamp*int64(time.Millisecond)), + Price: price, + Amount: amount, + Exchange: b.Name, + AssetType: asset.Spot, } case "ticker": var t TickerStream @@ -311,6 +308,11 @@ func (b *Binance) wsHandleData(respRaw []byte) error { err.Error()) } + pair, err := currency.NewPairFromFormattedPairs(t.Symbol, pairs, format) + if err != nil { + return err + } + b.Websocket.DataHandler <- &ticker.Price{ ExchangeName: b.Name, Open: t.OpenPrice, @@ -324,8 +326,7 @@ func (b *Binance) wsHandleData(respRaw []byte) error { Last: t.LastPrice, LastUpdated: time.Unix(0, t.EventTime*int64(time.Millisecond)), AssetType: asset.Spot, - Pair: currency.NewPairFromFormattedPairs(t.Symbol, b.GetEnabledPairs(asset.Spot), - b.GetPairFormat(asset.Spot, true)), + Pair: pair, } case "kline_1m", "kline_3m", "kline_5m", "kline_15m", "kline_30m", "kline_1h", "kline_2h", "kline_4h", "kline_6h", "kline_8h", "kline_12h", "kline_1d", "kline_3d", "kline_1w", "kline_1M": @@ -337,10 +338,14 @@ func (b *Binance) wsHandleData(respRaw []byte) error { err) } - b.Websocket.DataHandler <- wshandler.KlineData{ - Timestamp: time.Unix(0, kline.EventTime*int64(time.Millisecond)), - Pair: currency.NewPairFromFormattedPairs(kline.Symbol, b.GetEnabledPairs(asset.Spot), - b.GetPairFormat(asset.Spot, true)), + pair, err := currency.NewPairFromFormattedPairs(kline.Symbol, pairs, format) + if err != nil { + return err + } + + b.Websocket.DataHandler <- stream.KlineData{ + Timestamp: time.Unix(0, kline.EventTime*int64(time.Millisecond)), + Pair: pair, AssetType: asset.Spot, Exchange: b.Name, StartTime: time.Unix(0, kline.Kline.StartTime*int64(time.Millisecond)), @@ -361,22 +366,16 @@ func (b *Binance) wsHandleData(respRaw []byte) error { err) } - err = b.UpdateLocalCache(&depth) + err = b.UpdateLocalBuffer(&depth) if err != nil { return fmt.Errorf("%v - UpdateLocalCache error: %s", b.Name, err) } - - currencyPair := currency.NewPairFromFormattedPairs(depth.Pair, b.GetEnabledPairs(asset.Spot), - b.GetPairFormat(asset.Spot, true)) - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: currencyPair, - Asset: asset.Spot, - Exchange: b.Name, - } default: - b.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: b.Name + wshandler.UnhandledMessage + string(respRaw)} + b.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: b.Name + stream.UnhandledMessage + string(respRaw), + } } } } @@ -403,11 +402,15 @@ func stringToOrderStatus(status string) (order.Status, error) { // SeedLocalCache seeds depth data func (b *Binance) SeedLocalCache(p currency.Pair) error { - ob, err := b.GetOrderBook( - OrderBookDataRequestParams{ - Symbol: b.FormatExchangeCurrency(p, asset.Spot).String(), - Limit: 1000, - }) + fPair, err := b.FormatExchangeCurrency(p, asset.Spot) + if err != nil { + return err + } + + ob, err := b.GetOrderBook(OrderBookDataRequestParams{ + Symbol: fPair.String(), + Limit: 1000, + }) if err != nil { return err } @@ -439,11 +442,34 @@ func (b *Binance) SeedLocalCacheWithBook(p currency.Pair, orderbookNew *OrderBoo return b.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } -// UpdateLocalCache updates and returns the most recent iteration of the orderbook -func (b *Binance) UpdateLocalCache(wsdp *WebsocketDepthStream) error { - currencyPair := currency.NewPairFromFormattedPairs(wsdp.Pair, b.GetEnabledPairs(asset.Spot), - b.GetPairFormat(asset.Spot, true)) +// UpdateLocalBuffer updates and returns the most recent iteration of the orderbook +func (b *Binance) UpdateLocalBuffer(wsdp *WebsocketDepthStream) error { + enabledPairs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + + format, err := b.GetPairFormat(asset.Spot, true) + if err != nil { + return err + } + + currencyPair, err := currency.NewPairFromFormattedPairs(wsdp.Pair, + enabledPairs, + format) + if err != nil { + return err + } + currentBook := b.Websocket.Orderbook.GetOrderbook(currencyPair, asset.Spot) + if currentBook == nil { + // Used when a pair/s is enabled while connected + err = b.SeedLocalCache(currencyPair) + if err != nil { + return err + } + currentBook = b.Websocket.Orderbook.GetOrderbook(currencyPair, asset.Spot) + } // Drop any event where u is <= lastUpdateId in the snapshot. // The first processed event should have U <= lastUpdateId+1 AND u >= lastUpdateId+1. @@ -479,7 +505,7 @@ func (b *Binance) UpdateLocalCache(wsdp *WebsocketDepthStream) error { updateAsk = append(updateAsk, orderbook.Item{Price: p, Amount: a}) } - return b.Websocket.Orderbook.Update(&wsorderbook.WebsocketOrderbookUpdate{ + return b.Websocket.Orderbook.Update(&buffer.Update{ Bids: updateBid, Asks: updateAsk, Pair: currencyPair, @@ -487,3 +513,62 @@ func (b *Binance) UpdateLocalCache(wsdp *WebsocketDepthStream) error { Asset: asset.Spot, }) } + +// GenerateSubscriptions generates the default subscription set +func (b *Binance) GenerateSubscriptions() ([]stream.ChannelSubscription, error) { + var channels = []string{"@ticker", "@trade", "@kline_1m", "@depth@100ms"} + var subscriptions []stream.ChannelSubscription + assets := b.GetAssetTypes() + for x := range assets { + pairs, err := b.GetEnabledPairs(assets[x]) + if err != nil { + return nil, err + } + + for y := range pairs { + for z := range channels { + lp := pairs[y].Lower() + lp.Delimiter = "" + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: lp.String() + channels[z], + Currency: pairs[y], + Asset: assets[x], + }) + } + } + } + return subscriptions, nil +} + +// Subscribe subscribes to a set of channels +func (b *Binance) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + payload := WsPayload{ + Method: "SUBSCRIBE", + } + + for i := range channelsToSubscribe { + payload.Params = append(payload.Params, channelsToSubscribe[i].Channel) + } + err := b.Websocket.Conn.SendJSONMessage(payload) + if err != nil { + return err + } + b.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe...) + return nil +} + +// Unsubscribe unsubscribes from a set of channels +func (b *Binance) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + payload := WsPayload{ + Method: "UNSUBSCRIBE", + } + for i := range channelsToUnsubscribe { + payload.Params = append(payload.Params, channelsToUnsubscribe[i].Channel) + } + err := b.Websocket.Conn.SendJSONMessage(payload) + if err != nil { + return err + } + b.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe...) + return nil +} diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index d29c9039..ce6e5891 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -18,8 +18,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,21 +56,23 @@ func (b *Binance) SetDefaults() { b.API.CredentialsValidator.RequiresSecret = true b.SetValues() - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, + fmt1 := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{Uppercase: true}, ConfigFormat: ¤cy.PairFormat{ - Delimiter: "-", + Delimiter: currency.DashDelimiter, Uppercase: true, }, } + err := b.StoreAssetPairFormat(asset.Spot, fmt1) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + err = b.StoreAssetPairFormat(asset.Margin, fmt1) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + b.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ REST: true, @@ -145,7 +147,7 @@ func (b *Binance) SetDefaults() { b.API.Endpoints.URLDefault = apiURL b.API.Endpoints.URL = b.API.Endpoints.URLDefault - b.Websocket = wshandler.New() + b.Websocket = stream.New() b.API.Endpoints.WebsocketURL = binanceDefaultWebsocketURL b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout @@ -164,40 +166,31 @@ func (b *Binance) Setup(exch *config.ExchangeConfig) error { return err } - err = b.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: binanceDefaultWebsocketURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: b.WsConnect, - Features: &b.Features.Supports.WebsocketCapabilities, - }) - + err = b.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: binanceDefaultWebsocketURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: b.WsConnect, + Subscriber: b.Subscribe, + UnSubscriber: b.Unsubscribe, + GenerateSubscriptions: b.GenerateSubscriptions, + Features: &b.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + SortBuffer: true, + SortBufferByUpdateIDs: true, + }) if err != nil { return err } - b.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: b.Websocket.GetWebsocketURL(), - ProxyURL: b.Websocket.GetProxyAddress(), - Verbose: b.Verbose, + return b.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - b.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - true, - true, - false, - exch.Name) - return nil + }) } // Start starts the Binance go routine @@ -221,30 +214,59 @@ func (b *Binance) Run() { } forceUpdate := false - delim := b.GetPairFormat(asset.Spot, false).Delimiter - if !common.StringDataContains(b.GetEnabledPairs(asset.Spot).Strings(), delim) || - !common.StringDataContains(b.GetAvailablePairs(asset.Spot).Strings(), delim) { - enabledPairs := currency.NewPairsFromStrings( - []string{currency.BTC.String() + delim + currency.USDT.String()}, - ) - log.Warn(log.ExchangeSys, - "Available pairs for Binance reset due to config upgrade, please enable the ones you would like to use again") - forceUpdate = true + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", + b.Name, + err) + return + } + pairs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get enabled currencies. Err %s\n", + b.Name, + err) + return + } - err := b.UpdatePairs(enabledPairs, asset.Spot, true, true) + avail, err := b.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, "%s failed to get available currencies. Err %s\n", + b.Name, + err) + return + } + + if !common.StringDataContains(pairs.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var enabledPairs currency.Pairs + enabledPairs, err = currency.NewPairsFromStrings([]string{ + currency.BTC.String() + + format.Delimiter + + currency.USDT.String()}) if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update currencies. Err: %s\n", + log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err %s\n", b.Name, err) + } else { + log.Warn(log.ExchangeSys, + "Available pairs for Binance reset due to config upgrade, please enable the ones you would like to use again") + forceUpdate = true + + err = b.UpdatePairs(enabledPairs, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + b.Name, + err) + } } } if !b.GetEnabledFeatures().AutoPairUpdates && !forceUpdate { return } - - err := b.UpdateTradablePairs(forceUpdate) + err = b.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", @@ -254,36 +276,55 @@ func (b *Binance) Run() { } // FetchTradablePairs returns a list of the exchanges tradable pairs -func (b *Binance) FetchTradablePairs(asset asset.Item) ([]string, error) { - var validCurrencyPairs []string - +func (b *Binance) FetchTradablePairs(a asset.Item) ([]string, error) { info, err := b.GetExchangeInfo() if err != nil { return nil, err } + format, err := b.GetPairFormat(a, false) + if err != nil { + return nil, err + } + + var pairs []string for x := range info.Symbols { if info.Symbols[x].Status == "TRADING" { - validCurrencyPairs = append(validCurrencyPairs, info.Symbols[x].BaseAsset+ - b.GetPairFormat(asset, false).Delimiter+ - info.Symbols[x].QuoteAsset) + pair := info.Symbols[x].BaseAsset + + format.Delimiter + + info.Symbols[x].QuoteAsset + if a == asset.Spot && info.Symbols[x].IsSpotTradingAllowed { + pairs = append(pairs, pair) + } + if a == asset.Margin && info.Symbols[x].IsMarginTradingAllowed { + pairs = append(pairs, pair) + } } } - return validCurrencyPairs, nil + return pairs, nil } // UpdateTradablePairs updates the exchanges available pairs and stores // them in the exchanges config func (b *Binance) UpdateTradablePairs(forceUpdate bool) error { - pairs, err := b.FetchTradablePairs(asset.Spot) - if err != nil { - return err - } + assetTypes := b.GetAssetTypes() + for i := range assetTypes { + p, err := b.FetchTradablePairs(assetTypes[i]) + if err != nil { + return err + } - return b.UpdatePairs(currency.NewPairsFromStrings(pairs), - asset.Spot, - false, - forceUpdate) + pairs, err := currency.NewPairsFromStrings(p) + if err != nil { + return err + } + + err = b.UpdatePairs(pairs, assetTypes[i], false, forceUpdate) + if err != nil { + return err + } + } + return nil } // UpdateTicker updates and returns the ticker for a currency pair @@ -292,28 +333,39 @@ func (b *Binance) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.P if err != nil { return nil, err } - pairs := b.GetEnabledPairs(assetType) + + pairs, err := b.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + for i := range pairs { for y := range tick { - pairFmt := b.FormatExchangeCurrency(pairs[i], assetType).String() - if tick[y].Symbol != pairFmt { + pairFmt, err := b.FormatExchangeCurrency(pairs[i], assetType) + if err != nil { + return nil, err + } + + if tick[y].Symbol != pairFmt.String() { continue } - tickerPrice := &ticker.Price{ - Last: tick[y].LastPrice, - High: tick[y].HighPrice, - Low: tick[y].LowPrice, - Bid: tick[y].BidPrice, - Ask: tick[y].AskPrice, - Volume: tick[y].Volume, - QuoteVolume: tick[y].QuoteVolume, - Open: tick[y].OpenPrice, - Close: tick[y].PrevClosePrice, - Pair: pairs[i], - } - err = ticker.ProcessTicker(b.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick[y].LastPrice, + High: tick[y].HighPrice, + Low: tick[y].LowPrice, + Bid: tick[y].BidPrice, + Ask: tick[y].AskPrice, + Volume: tick[y].Volume, + QuoteVolume: tick[y].QuoteVolume, + Open: tick[y].OpenPrice, + Close: tick[y].PrevClosePrice, + Pair: pairs[i], + ExchangeName: b.Name, + AssetType: assetType, + }) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } @@ -340,13 +392,19 @@ func (b *Binance) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderb // UpdateOrderbook updates and returns the orderbook for a currency pair func (b *Binance) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - orderbookNew, err := b.GetOrderBook(OrderBookDataRequestParams{Symbol: b.FormatExchangeCurrency(p, - assetType).String(), Limit: 1000}) + fpair, err := b.FormatExchangeCurrency(p, assetType) if err != nil { - return orderBook, err + return nil, err } + orderbookNew, err := b.GetOrderBook(OrderBookDataRequestParams{ + Symbol: fpair.String(), + Limit: 1000}) + if err != nil { + return nil, err + } + + orderBook := new(orderbook.Base) for x := range orderbookNew.Bids { orderBook.Bids = append(orderBook.Bids, orderbook.Item{ @@ -499,8 +557,12 @@ func (b *Binance) CancelOrder(order *order.Cancel) error { return err } - _, err = b.CancelExistingOrder(b.FormatExchangeCurrency(order.Pair, - order.AssetType).String(), + fpair, err := b.FormatExchangeCurrency(order.Pair, order.AssetType) + if err != nil { + return err + } + + _, err = b.CancelExistingOrder(fpair.String(), orderIDInt, order.AccountID) return err @@ -567,11 +629,6 @@ func (b *Binance) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (b *Binance) GetWebsocket() (*wshandler.Websocket, error) { - return b.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (b *Binance) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if (!b.AllowAuthenticatedRequest() || b.SkipAuthCheck) && // Todo check connection status @@ -589,8 +646,13 @@ func (b *Binance) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, var orders []order.Detail for x := range req.Pairs { - resp, err := b.OpenOrders(b.FormatExchangeCurrency(req.Pairs[x], - asset.Spot).String()) + fpair, err := b.FormatExchangeCurrency(req.Pairs[x], + asset.Spot) + if err != nil { + return nil, err + } + + resp, err := b.OpenOrders(fpair.String()) if err != nil { return nil, err } @@ -600,6 +662,11 @@ func (b *Binance) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, orderType := order.Type(strings.ToUpper(resp[i].Type)) orderDate := time.Unix(0, int64(resp[i].Time)*int64(time.Millisecond)) + pair, err := currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } + orders = append(orders, order.Detail{ Amount: resp[i].OrigQty, Date: orderDate, @@ -609,7 +676,7 @@ func (b *Binance) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, Type: orderType, Price: resp[i].Price, Status: order.Status(resp[i].Status), - Pair: currency.NewPairFromString(resp[i].Symbol), + Pair: pair, }) } } @@ -629,8 +696,11 @@ func (b *Binance) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, var orders []order.Detail for x := range req.Pairs { - resp, err := b.AllOrders(b.FormatExchangeCurrency(req.Pairs[x], - asset.Spot).String(), + fpair, err := b.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } + resp, err := b.AllOrders(fpair.String(), "", "1000") if err != nil { @@ -646,6 +716,11 @@ func (b *Binance) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, continue } + pair, err := currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } + orders = append(orders, order.Detail{ Amount: resp[i].OrigQty, Date: orderDate, @@ -654,7 +729,7 @@ func (b *Binance) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, Side: orderSide, Type: orderType, Price: resp[i].Price, - Pair: currency.NewPairFromString(resp[i].Symbol), + Pair: pair, Status: order.Status(resp[i].Status), }) } @@ -666,28 +741,6 @@ func (b *Binance) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *Binance) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *Binance) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *Binance) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return b.Websocket.GetSubscriptions(), nil -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (b *Binance) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (b *Binance) ValidateCredentials() error { @@ -718,9 +771,13 @@ func (b *Binance) GetHistoricCandles(pair currency.Pair, a asset.Item, start, en return kline.Item{}, errors.New(kline.ErrRequestExceedsExchangeLimits) } + fpair, err := b.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } req := KlinesRequestParams{ Interval: b.FormatExchangeKlineInterval(interval), - Symbol: b.FormatExchangeCurrency(pair, a).String(), + Symbol: fpair.String(), StartTime: start.Unix() * 1000, EndTime: end.Unix() * 1000, Limit: int(b.Features.Enabled.Kline.ResultLimit), @@ -768,11 +825,16 @@ func (b *Binance) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, s Interval: interval, } + formattedPair, err := b.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + dates := kline.CalcDateRanges(start, end, interval, b.Features.Enabled.Kline.ResultLimit) for x := range dates { req := KlinesRequestParams{ Interval: b.FormatExchangeKlineInterval(interval), - Symbol: b.FormatExchangeCurrency(pair, a).String(), + Symbol: formattedPair.String(), StartTime: dates[x].Start.UTC().Unix() * 1000, EndTime: dates[x].End.UTC().Unix() * 1000, Limit: int(b.Features.Enabled.Kline.ResultLimit), diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go index 82cef9d4..dc079db9 100644 --- a/exchanges/bitfinex/bitfinex.go +++ b/exchanges/bitfinex/bitfinex.go @@ -17,7 +17,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -83,9 +82,7 @@ const ( // Bitfinex is the overarching type across the bitfinex package type Bitfinex struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection - AuthenticatedWebsocketConn *wshandler.WebsocketConnection - WebsocketSubdChannels map[int]WebsocketChanInfo + WebsocketSubdChannels map[int]WebsocketChanInfo } // GetPlatformStatus returns the Bifinex platform status @@ -778,7 +775,7 @@ func (b *Bitfinex) WithdrawFIAT(withdrawalType, walletType string, withdrawReque // Major Upgrade needed on this function to include all query params func (b *Bitfinex) NewOrder(currencyPair, orderType string, amount, price float64, buy, hidden bool) (Order, error) { if !common.StringDataCompare(AcceptedOrderType, orderType) { - return Order{}, errors.New("order type not accepted") + return Order{}, fmt.Errorf("order type %s not accepted", orderType) } response := Order{} diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index 0efd5788..139ebe08 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -16,7 +16,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/portfolio/banking" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -42,6 +41,7 @@ func TestMain(m *testing.M) { if err != nil { log.Fatal("Bitfinex Setup() init error") } + b.Websocket = sharedtestvalues.NewTestWebsocket() err = b.Setup(bfxConfig) if err != nil { log.Fatal("Bitfinex setup error", err) @@ -58,11 +58,32 @@ func TestMain(m *testing.M) { b.API.AuthenticatedWebsocketSupport = true } b.WebsocketSubdChannels = make(map[int]WebsocketChanInfo) - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } +func TestAppendOptionalDelimiter(t *testing.T) { + t.Parallel() + curr1, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + b.appendOptionalDelimiter(&curr1) + if curr1.Delimiter != "" { + t.Errorf("Expected no delimiter, received %v", curr1.Delimiter) + } + curr2, err := currency.NewPairFromString("DUSK:USD") + if err != nil { + t.Fatal(err) + } + + curr2.Delimiter = "" + b.appendOptionalDelimiter(&curr2) + if curr2.Delimiter != ":" { + t.Errorf("Expected \":\" as a delimiter, received %v", curr2.Delimiter) + } +} + func TestGetPlatformStatus(t *testing.T) { t.Parallel() result, err := b.GetPlatformStatus() @@ -320,7 +341,7 @@ func TestNewOrder(t *testing.T) { _, err := b.NewOrder("BTCUSD", order.Limit.Lower(), - 1, + -1, 2, false, true) @@ -330,27 +351,17 @@ func TestNewOrder(t *testing.T) { } func TestUpdateTicker(t *testing.T) { - _, err := b.UpdateTicker(currency.NewPairFromString("BTCUSD"), asset.Spot) + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + + _, err = b.UpdateTicker(pair, asset.Spot) if err != nil { t.Error(err) } } -func TestAppendOptionalDelimiter(t *testing.T) { - t.Parallel() - curr1 := currency.NewPairFromString("BTCUSD") - b.appendOptionalDelimiter(&curr1) - if curr1.Delimiter != "" { - t.Errorf("Expected no delimiter, received %v", curr1.Delimiter) - } - curr2 := currency.NewPairFromString("DUSK:USD") - curr2.Delimiter = "" - b.appendOptionalDelimiter(&curr2) - if curr2.Delimiter != ":" { - t.Errorf("Expected \"-\" as a delimiter, received %v", curr2.Delimiter) - } -} - func TestNewOrderMulti(t *testing.T) { if !b.ValidateAPICredentials() { t.SkipNow() @@ -762,19 +773,20 @@ func TestSubmitOrder(t *testing.T) { var orderSubmission = &order.Submit{ Pair: currency.Pair{ Delimiter: "_", - Base: currency.BTC, + Base: currency.XRP, Quote: currency.USD, }, - Side: order.Buy, - Type: order.Limit, - Price: 1, - Amount: 1, - ClientID: "meowOrder", + AssetType: asset.Spot, + Side: order.Sell, + Type: order.Limit, + Price: 1000, + Amount: 20, + ClientID: "meowOrder", } response, err := b.SubmitOrder(orderSubmission) if areTestAPIKeysSet() && err != nil { - t.Errorf("Could not cancel orders: %v", err) + t.Errorf("Could not place order: %v", err) } if areTestAPIKeysSet() && !response.IsOrderPlaced { t.Error("Order not placed") @@ -944,21 +956,12 @@ func TestGetDepositAddress(t *testing.T) { } func setupWs() { - b.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: authenticatedBitfinexWebsocketEndpoint, - Verbose: b.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, - } var dialer websocket.Dialer - err := b.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{}) + err := b.Websocket.AuthConn.Dial(&dialer, http.Header{}) if err != nil { log.Fatal(err) } - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() - go b.wsReadData(b.AuthenticatedWebsocketConn) + go b.wsReadData(b.Websocket.AuthConn) go b.WsDataHandler() } @@ -1001,12 +1004,13 @@ func TestWsPlaceOrder(t *testing.T) { if !wsAuthExecuted { runAuth(t) } + _, err := b.WsNewOrder(&WsNewOrderRequest{ - CustomID: 1337, - Type: order.Buy.String(), - Symbol: "tBTCUSD", - Amount: 10, - Price: -10, + GroupID: 1, + Type: "EXCHANGE LIMIT", + Symbol: "tXRPUSD", + Amount: -20, + Price: 1000, }) if err != nil { t.Error(err) @@ -1169,6 +1173,24 @@ func TestWsTickerResponse(t *testing.T) { if err != nil { t.Error(err) } + b.WebsocketSubdChannels[123412] = WebsocketChanInfo{Pair: "XAUTF0:USTF0", Channel: wsTicker} + pressXToJSON = `[123412,[61.304,2228.36155358,61.305,1323.2442970500003,0.395,0.0065,61.371,50973.3020771,62.5,57.421]]` + err = b.wsHandleData([]byte(pressXToJSON)) + if err != nil { + t.Error(err) + } + b.WebsocketSubdChannels[123413] = WebsocketChanInfo{Pair: "trade:1m:tXRPUSD", Channel: wsTicker} + pressXToJSON = `[123413,[61.304,2228.36155358,61.305,1323.2442970500003,0.395,0.0065,61.371,50973.3020771,62.5,57.421]]` + err = b.wsHandleData([]byte(pressXToJSON)) + if err != nil { + t.Error(err) + } + b.WebsocketSubdChannels[123414] = WebsocketChanInfo{Pair: "trade:1m:fZRX:p30", Channel: wsTicker} + pressXToJSON = `[123414,[61.304,2228.36155358,61.305,1323.2442970500003,0.395,0.0065,61.371,50973.3020771,62.5,57.421]]` + err = b.wsHandleData([]byte(pressXToJSON)) + if err != nil { + t.Error(err) + } } func TestWsCandleResponse(t *testing.T) { @@ -1186,6 +1208,7 @@ func TestWsCandleResponse(t *testing.T) { } func TestWsOrderSnapshot(t *testing.T) { + b.WsAddSubscriptionChannel(0, "account", "N/A") pressXToJSON := `[0,"os",[[34930659963,null,1574955083558,"tETHUSD",1574955083558,1574955083573,0.201104,0.201104,"EXCHANGE LIMIT",null,null,null,0,"ACTIVE",null,null,120,0,0,0,null,null,null,0,0,null,null,null,"BFX",null,null,null]]]` err := b.wsHandleData([]byte(pressXToJSON)) if err != nil { @@ -1213,9 +1236,12 @@ func TestWsNotifications(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSD") + currencyPair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 24) - _, err := b.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = b.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } @@ -1227,9 +1253,12 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("TBTCUSD") + currencyPair, err := currency.NewPairFromString("TBTCUSD") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 24) - _, err := b.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) + _, err = b.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) if err != nil { t.Fatal(err) } @@ -1241,42 +1270,95 @@ func TestGetHistoricCandlesExtended(t *testing.T) { } func TestFixCasing(t *testing.T) { - ret := b.fixCasing(currency.NewPairFromString("BTCUSD"), asset.Spot) + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + ret, err := b.fixCasing(pair, asset.Spot) + if err != nil { + t.Fatal(err) + } if ret != "tBTCUSD" { t.Errorf("unexpected result: %v", ret) } - - ret = b.fixCasing(currency.NewPairFromString("TBTCUSD"), asset.Spot) + pair, err = currency.NewPairFromString("TBTCUSD") + if err != nil { + t.Fatal(err) + } + ret, err = b.fixCasing(pair, asset.Spot) + if err != nil { + t.Fatal(err) + } if ret != "tBTCUSD" { t.Errorf("unexpected result: %v", ret) } - - ret = b.fixCasing(currency.NewPairFromString("tBTCUSD"), asset.Spot) + pair, err = currency.NewPairFromString("tBTCUSD") + if err != nil { + t.Fatal(err) + } + ret, err = b.fixCasing(pair, asset.Spot) + if err != nil { + t.Fatal(err) + } + if err != nil { + t.Fatal(err) + } if ret != "tBTCUSD" { t.Errorf("unexpected result: %v", ret) } - - ret = b.fixCasing(currency.NewPairFromString("BTCUSD"), asset.Margin) + pair, err = currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + ret, err = b.fixCasing(pair, asset.Margin) + if err != nil { + t.Fatal(err) + } if ret != "fBTCUSD" { t.Errorf("unexpected result: %v", ret) } - - ret = b.fixCasing(currency.NewPairFromString("BTCUSD"), asset.Spot) + pair, err = currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + ret, err = b.fixCasing(pair, asset.Spot) + if err != nil { + t.Fatal(err) + } if ret != "tBTCUSD" { t.Errorf("unexpected result: %v", ret) } - - ret = b.fixCasing(currency.NewPairFromString("FUNETH"), asset.Spot) + pair, err = currency.NewPairFromString("FUNETH") + if err != nil { + t.Fatal(err) + } + ret, err = b.fixCasing(pair, asset.Spot) + if err != nil { + t.Fatal(err) + } if ret != "tFUNETH" { t.Errorf("unexpected result: %v", ret) } - - ret = b.fixCasing(currency.NewPairFromString("TNBUSD"), asset.Spot) + pair, err = currency.NewPairFromString("TNBUSD") + if err != nil { + t.Fatal(err) + } + ret, err = b.fixCasing(pair, asset.Spot) + if err != nil { + t.Fatal(err) + } if ret != "tTNBUSD" { t.Errorf("unexpected result: %v", ret) } - ret = b.fixCasing(currency.NewPairFromString("tTNBUSD"), asset.Spot) + pair, err = currency.NewPairFromString("tTNBUSD") + if err != nil { + t.Fatal(err) + } + ret, err = b.fixCasing(pair, asset.Spot) + if err != nil { + t.Fatal(err) + } if ret != "tTNBUSD" { t.Errorf("unexpected result: %v", ret) } diff --git a/exchanges/bitfinex/bitfinex_types.go b/exchanges/bitfinex/bitfinex_types.go index 6752c973..e79a1f32 100644 --- a/exchanges/bitfinex/bitfinex_types.go +++ b/exchanges/bitfinex/bitfinex_types.go @@ -374,9 +374,11 @@ type WebsocketChanInfo struct { // WebsocketBook holds booking information type WebsocketBook struct { - Price float64 ID int64 + Price float64 Amount float64 + Rate float64 + Period int64 } // WebsocketTrade holds trade information diff --git a/exchanges/bitfinex/bitfinex_websocket.go b/exchanges/bitfinex/bitfinex_websocket.go index 13dd78c0..5a227627 100644 --- a/exchanges/bitfinex/bitfinex_websocket.go +++ b/exchanges/bitfinex/bitfinex_websocket.go @@ -10,69 +10,74 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" "github.com/thrasher-corp/gocryptotrader/log" ) -var comms = make(chan wshandler.WebsocketResponse) +var comms = make(chan stream.Response) // WsConnect starts a new websocket connection func (b *Bitfinex) WsConnect() error { if !b.Websocket.IsEnabled() || !b.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := b.WebsocketConn.Dial(&dialer, http.Header{}) + err := b.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { - return fmt.Errorf("%v unable to connect to Websocket. Error: %s", b.Name, err) + return fmt.Errorf("%v unable to connect to Websocket. Error: %s", + b.Name, + err) } - go b.wsReadData(b.WebsocketConn) + go b.wsReadData(b.Websocket.Conn) if b.Websocket.CanUseAuthenticatedEndpoints() { - err = b.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{}) + err = b.Websocket.AuthConn.Dial(&dialer, http.Header{}) if err != nil { - log.Errorf(log.ExchangeSys, "%v unable to connect to authenticated Websocket. Error: %s", b.Name, err) + log.Errorf(log.ExchangeSys, + "%v unable to connect to authenticated Websocket. Error: %s", + b.Name, + err) b.Websocket.SetCanUseAuthenticatedEndpoints(false) } - go b.wsReadData(b.AuthenticatedWebsocketConn) + go b.wsReadData(b.Websocket.AuthConn) err = b.WsSendAuth() if err != nil { - log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", b.Name, err) + log.Errorf(log.ExchangeSys, + "%v - authentication failed: %v\n", + b.Name, + err) b.Websocket.SetCanUseAuthenticatedEndpoints(false) } } - b.GenerateDefaultSubscriptions() + subs, err := b.GenerateDefaultSubscriptions() + if err != nil { + return err + } go b.WsDataHandler() - return nil + return b.Websocket.SubscribeToChannels(subs) } // wsReadData receives and passes on websocket messages for processing -func (b *Bitfinex) wsReadData(ws *wshandler.WebsocketConnection) { +func (b *Bitfinex) wsReadData(ws stream.Connection) { b.Websocket.Wg.Add(1) defer b.Websocket.Wg.Done() for { - select { - case <-b.Websocket.ShutdownC: + resp := ws.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := ws.ReadMessage() - if err != nil { - b.Websocket.ReadMessageErrors <- err - return - } - b.Websocket.TrafficAlert <- struct{}{} - comms <- resp } + comms <- resp } } @@ -82,8 +87,6 @@ func (b *Bitfinex) WsDataHandler() { defer b.Websocket.Wg.Done() for { select { - case <-b.Websocket.ShutdownC: - return case resp := <-comms: if resp.Type == websocket.TextMessage { err := b.wsHandleData(resp.Raw) @@ -91,6 +94,8 @@ func (b *Bitfinex) WsDataHandler() { b.Websocket.DataHandler <- err } } + case <-b.Websocket.ShutdownC: + return } } } @@ -106,12 +111,21 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { event := d["event"] switch event { case "subscribed": - if symbol, ok := d["pair"].(string); ok { + if symbol, ok := d["symbol"].(string); ok { b.WsAddSubscriptionChannel(int(d["chanId"].(float64)), d["channel"].(string), symbol, ) } else if key, ok := d["key"].(string); ok { + // Capture trading subscriptions + contents := strings.Split(d["key"].(string), ":") + if len(contents) > 3 { + // Edge case to parse margin strings. + // map[chanId:139136 channel:candles event:subscribed key:trade:1m:tXAUTF0:USTF0] + if contents[2][0] == 't' { + key = contents[2] + ":" + contents[3] + } + } b.WsAddSubscriptionChannel(int(d["chanId"].(float64)), d["channel"].(string), key, @@ -134,6 +148,7 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { return nil } } + chanID := int(d[0].(float64)) chanInfo, ok := b.WebsocketSubdChannels[chanID] if !ok && chanID != 0 { @@ -141,67 +156,122 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { chanID) } + var chanAsset = asset.Spot + var pair currency.Pair + pairInfo := strings.Split(chanInfo.Pair, ":") + switch { + case len(pairInfo) >= 3: + newPair := pairInfo[2] + if newPair[0] == 'f' { + chanAsset = asset.MarginFunding + } + + pair, err = currency.NewPairFromString(newPair[1:]) + if err != nil { + return err + } + case len(pairInfo) == 1: + newPair := pairInfo[0] + if newPair[0] == 'f' { + chanAsset = asset.MarginFunding + } + + pair, err = currency.NewPairFromString(newPair[1:]) + if err != nil { + return err + } + case chanInfo.Pair != "": + if strings.Contains(chanInfo.Pair, ":") { + chanAsset = asset.Margin + } + + pair, err = currency.NewPairFromString(chanInfo.Pair[1:]) + if err != nil { + return err + } + } + switch chanInfo.Channel { case wsBook: var newOrderbook []WebsocketBook - curr := currency.NewPairFromString(chanInfo.Pair) - if obSnapBundle, ok := d[1].([]interface{}); ok { - switch id := obSnapBundle[0].(type) { - case []interface{}: - for i := range obSnapBundle { - data := obSnapBundle[i].([]interface{}) + obSnapBundle, ok := d[1].([]interface{}) + if !ok { + return errors.New("orderbook interface cast failed") + } + + switch id := obSnapBundle[0].(type) { + case []interface{}: + for i := range obSnapBundle { + data := obSnapBundle[i].([]interface{}) + if len(data) == 4 { + newOrderbook = append(newOrderbook, WebsocketBook{ + ID: int64(data[0].(float64)), + Period: int64(data[1].(float64)), + Rate: data[2].(float64), + Amount: data[3].(float64)}) + } else { newOrderbook = append(newOrderbook, WebsocketBook{ ID: int64(data[0].(float64)), Price: data[1].(float64), Amount: data[2].(float64)}) } - err := b.WsInsertSnapshot(curr, - asset.Spot, - newOrderbook) - if err != nil { - return fmt.Errorf("bitfinex_websocket.go inserting snapshot error: %s", - err) - } - case float64: + } + err := b.WsInsertSnapshot(pair, chanAsset, newOrderbook) + if err != nil { + return fmt.Errorf("bitfinex_websocket.go inserting snapshot error: %s", + err) + } + case float64: + if len(obSnapBundle) == 4 { + newOrderbook = append(newOrderbook, WebsocketBook{ + ID: int64(id), + Period: int64(obSnapBundle[1].(float64)), + Rate: obSnapBundle[2].(float64), + Amount: obSnapBundle[3].(float64)}) + } else { newOrderbook = append(newOrderbook, WebsocketBook{ ID: int64(id), Price: obSnapBundle[1].(float64), Amount: obSnapBundle[2].(float64)}) - err := b.WsUpdateOrderbook(curr, - asset.Spot, - newOrderbook) - if err != nil { - return fmt.Errorf("bitfinex_websocket.go inserting snapshot error: %s", - err) - } + } + + err := b.WsUpdateOrderbook(pair, chanAsset, newOrderbook) + if err != nil { + return fmt.Errorf("bitfinex_websocket.go updating orderbook error: %s", + err) } } + return nil case wsCandles: - curr := currency.NewPairFromString(chanInfo.Pair) if candleBundle, ok := d[1].([]interface{}); ok { if len(candleBundle) == 0 { return nil } + switch candleData := candleBundle[0].(type) { case []interface{}: - b.Websocket.DataHandler <- wshandler.KlineData{ - Timestamp: time.Unix(0, int64(candleData[0].(float64))), - Exchange: b.Name, - AssetType: asset.Spot, - Pair: curr, - OpenPrice: candleData[1].(float64), - ClosePrice: candleData[2].(float64), - HighPrice: candleData[3].(float64), - LowPrice: candleData[4].(float64), - Volume: candleData[5].(float64), + for i := range candleBundle { + element := candleBundle[i].([]interface{}) + b.Websocket.DataHandler <- stream.KlineData{ + Timestamp: time.Unix(0, int64(element[0].(float64))*int64(time.Millisecond)), + Exchange: b.Name, + AssetType: chanAsset, + Pair: pair, + OpenPrice: element[1].(float64), + ClosePrice: element[2].(float64), + HighPrice: element[3].(float64), + LowPrice: element[4].(float64), + Volume: element[5].(float64), + } } + case float64: - b.Websocket.DataHandler <- wshandler.KlineData{ - Timestamp: time.Unix(0, int64(candleData)), + b.Websocket.DataHandler <- stream.KlineData{ + Timestamp: time.Unix(0, int64(candleData)*int64(time.Millisecond)), Exchange: b.Name, - AssetType: asset.Spot, - Pair: curr, + AssetType: chanAsset, + Pair: pair, OpenPrice: candleBundle[1].(float64), ClosePrice: candleBundle[2].(float64), HighPrice: candleBundle[3].(float64), @@ -221,8 +291,8 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { Volume: tickerData[7].(float64), High: tickerData[8].(float64), Low: tickerData[9].(float64), - AssetType: asset.Spot, - Pair: currency.NewPairFromString(chanInfo.Pair), + AssetType: chanAsset, + Pair: pair, } return nil case wsTrades: @@ -233,22 +303,20 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { for i := range snapshot { elem := snapshot[i].([]interface{}) if len(elem) == 5 { - trades = append(trades, - WebsocketTrade{ - ID: int64(elem[0].(float64)), - Timestamp: int64(elem[1].(float64)), - Amount: elem[2].(float64), - Rate: elem[3].(float64), - Period: int64(elem[4].(float64)), - }) + trades = append(trades, WebsocketTrade{ + ID: int64(elem[0].(float64)), + Timestamp: int64(elem[1].(float64)), + Amount: elem[2].(float64), + Rate: elem[3].(float64), + Period: int64(elem[4].(float64)), + }) } else { - trades = append(trades, - WebsocketTrade{ - ID: int64(elem[0].(float64)), - Timestamp: int64(elem[1].(float64)), - Amount: elem[2].(float64), - Price: elem[3].(float64), - }) + trades = append(trades, WebsocketTrade{ + ID: int64(elem[0].(float64)), + Timestamp: int64(elem[1].(float64)), + Amount: elem[2].(float64), + Price: elem[3].(float64), + }) } } case 3: @@ -260,11 +328,22 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { return nil } data := d[2].([]interface{}) - trades = append(trades, WebsocketTrade{ - ID: int64(data[0].(float64)), - Timestamp: int64(data[1].(float64)), - Price: data[3].(float64), - Amount: data[2].(float64)}) + if len(data) == 5 { + trades = append(trades, WebsocketTrade{ + ID: int64(data[0].(float64)), + Timestamp: int64(data[1].(float64)), + Amount: data[2].(float64), + Rate: data[3].(float64), + Period: int64(data[4].(float64)), + }) + } else { + trades = append(trades, WebsocketTrade{ + ID: int64(data[0].(float64)), + Timestamp: int64(data[1].(float64)), + Amount: data[2].(float64), + Price: data[3].(float64), + }) + } } for i := range trades { @@ -276,29 +355,30 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { } if trades[i].Rate > 0 { - b.Websocket.DataHandler <- wshandler.FundingData{ - CurrencyPair: currency.NewPairFromString(chanInfo.Pair), + b.Websocket.DataHandler <- stream.FundingData{ + CurrencyPair: pair, Timestamp: time.Unix(0, trades[i].Timestamp*int64(time.Millisecond)), Amount: newAmount, Exchange: b.Name, - AssetType: asset.Spot, + AssetType: chanAsset, Side: side, Rate: trades[i].Rate, Period: trades[i].Period, } - return nil + continue } - b.Websocket.DataHandler <- wshandler.TradeData{ - CurrencyPair: currency.NewPairFromString(chanInfo.Pair), + b.Websocket.DataHandler <- stream.TradeData{ + CurrencyPair: pair, Timestamp: time.Unix(0, trades[i].Timestamp*int64(time.Millisecond)), Price: trades[i].Price, Amount: newAmount, Exchange: b.Name, - AssetType: asset.Spot, + AssetType: chanAsset, Side: side, } } + return nil } if authResp, ok := d[1].(string); ok { @@ -315,8 +395,7 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { strings.Contains(channelName, wsFundingOrderCancelRequest): if data[0] != nil && data[0].(float64) > 0 { id := int64(data[0].(float64)) - if b.WebsocketConn.IsIDWaitingForResponse(id) { - b.AuthenticatedWebsocketConn.SetResponseIDAndData(id, respRaw) + if b.Websocket.Match.IncomingWithData(id, respRaw) { return nil } b.wsHandleFundingOffer(data) @@ -326,19 +405,23 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { strings.Contains(channelName, wsOrderCancelRequest): if data[2] != nil && data[2].(float64) > 0 { id := int64(data[2].(float64)) - if b.WebsocketConn.IsIDWaitingForResponse(id) { - b.AuthenticatedWebsocketConn.SetResponseIDAndData(id, respRaw) + if b.Websocket.Match.IncomingWithData(id, respRaw) { return nil } b.wsHandleOrder(data) } default: - return fmt.Errorf("%s - Unexpected data returned %s", b.Name, respRaw) + return fmt.Errorf("%s - Unexpected data returned %s", + b.Name, + respRaw) } } - if notification[5] != nil && strings.EqualFold(notification[5].(string), wsError) { - return fmt.Errorf("%s - Error %s", b.Name, notification[6].(string)) + if notification[5] != nil && + strings.EqualFold(notification[5].(string), wsError) { + return fmt.Errorf("%s - Error %s", + b.Name, + notification[6].(string)) } case wsOrderSnapshot: if snapBundle, ok := d[2].([]interface{}); ok && len(snapBundle) > 0 { @@ -634,7 +717,9 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { } } default: - b.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: b.Name + wshandler.UnhandledMessage + string(respRaw)} + b.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: b.Name + stream.UnhandledMessage + string(respRaw), + } return nil } } @@ -781,20 +866,13 @@ func (b *Bitfinex) WsInsertSnapshot(p currency.Pair, assetType asset.Item, books newOrderBook.Pair = p newOrderBook.ExchangeName = b.Name - err := b.Websocket.Orderbook.LoadSnapshot(&newOrderBook) - if err != nil { - return fmt.Errorf("bitfinex.go error - %s", err) - } - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{Pair: p, - Asset: assetType, - Exchange: b.Name} - return nil + return b.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } // WsUpdateOrderbook updates the orderbook list, removing and adding to the // orderbook sides func (b *Bitfinex) WsUpdateOrderbook(p currency.Pair, assetType asset.Item, book []WebsocketBook) error { - orderbookUpdate := wsorderbook.WebsocketOrderbookUpdate{ + orderbookUpdate := buffer.Update{ Asset: assetType, Pair: p, } @@ -833,97 +911,115 @@ func (b *Bitfinex) WsUpdateOrderbook(p currency.Pair, assetType asset.Item, book } } } - err := b.Websocket.Orderbook.Update(&orderbookUpdate) - if err != nil { - return err - } - - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{Pair: p, - Asset: assetType, - Exchange: b.Name} - - return nil + return b.Websocket.Orderbook.Update(&orderbookUpdate) } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (b *Bitfinex) GenerateDefaultSubscriptions() { +func (b *Bitfinex) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { var channels = []string{ wsBook, wsTrades, wsTicker, wsCandles, } - var subscriptions []wshandler.WebsocketChannelSubscription - for i := range channels { - enabledPairs := b.GetEnabledPairs(asset.Spot) - for j := range enabledPairs { - if strings.HasPrefix(enabledPairs[j].Base.String(), "f") { - log.Warnf(log.WebsocketMgr, - "%v - Websocket does not support funding currency %v, skipping", - b.Name, enabledPairs[j]) - continue - } - b.appendOptionalDelimiter(&enabledPairs[j]) - params := make(map[string]interface{}) - if channels[i] == wsBook { - params["prec"] = "R0" - params["len"] = "100" - } - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: channels[i], - Currency: enabledPairs[j], - Params: params, - }) + var subscriptions []stream.ChannelSubscription + assets := b.GetAssetTypes() + for i := range assets { + enabledPairs, err := b.GetEnabledPairs(assets[i]) + if err != nil { + return nil, err + } + + for j := range channels { + for k := range enabledPairs { + params := make(map[string]interface{}) + if channels[j] == wsBook { + params["prec"] = "R0" + params["len"] = "100" + } + + if channels[j] == wsCandles { + // TODO: Add ability to select timescale && funding period + var fundingPeriod string + prefix := "t" + if assets[i] == asset.MarginFunding { + prefix = "f" + fundingPeriod = ":p30" + } + params["key"] = "trade:1m:" + prefix + enabledPairs[k].String() + fundingPeriod + } else { + params["symbol"] = enabledPairs[k].String() + } + + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: channels[j], + Currency: enabledPairs[k], + Params: params, + Asset: assets[i], + }) + } } } - b.Websocket.SubscribeToChannels(subscriptions) + + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (b *Bitfinex) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - req := make(map[string]interface{}) - req["event"] = "subscribe" - req["channel"] = channelToSubscribe.Channel +func (b *Bitfinex) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + req := make(map[string]interface{}) + req["event"] = "subscribe" + req["channel"] = channelsToSubscribe[i].Channel - if channelToSubscribe.Currency.String() != "" { - if channelToSubscribe.Channel == wsCandles { - // TODO: Add ability to select timescale - req["key"] = fmt.Sprintf("trade:1D:%v", - b.FormatExchangeCurrency(channelToSubscribe.Currency, asset.Spot).String()) - } else { - req["symbol"] = b.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String() - } - } - - if len(channelToSubscribe.Params) > 0 { - for k, v := range channelToSubscribe.Params { + for k, v := range channelsToSubscribe[i].Params { req[k] = v } - } - return b.WebsocketConn.SendJSONMessage(req) + err := b.Websocket.Conn.SendJSONMessage(req) + if err != nil { + errs = append(errs, err) + continue + } + b.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) + } + if errs != nil { + return errs + } + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (b *Bitfinex) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - req := make(map[string]interface{}) - req["event"] = "unsubscribe" - req["channel"] = channelToSubscribe.Channel +func (b *Bitfinex) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToUnsubscribe { + req := make(map[string]interface{}) + req["event"] = "unsubscribe" + req["channel"] = channelsToUnsubscribe[i].Channel - if len(channelToSubscribe.Params) > 0 { - for k, v := range channelToSubscribe.Params { + for k, v := range channelsToUnsubscribe[i].Params { req[k] = v } + + err := b.Websocket.Conn.SendJSONMessage(req) + if err != nil { + errs = append(errs, err) + continue + } + b.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) } - return b.WebsocketConn.SendJSONMessage(req) + if errs != nil { + return errs + } + return nil } // WsSendAuth sends a autheticated event payload func (b *Bitfinex) WsSendAuth() error { if !b.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", b.Name) + return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", + b.Name) } nonce := strconv.FormatInt(time.Now().Unix(), 10) payload := "AUTH" + nonce @@ -931,15 +1027,13 @@ func (b *Bitfinex) WsSendAuth() error { Event: "auth", APIKey: b.API.Credentials.Key, AuthPayload: payload, - AuthSig: crypto.HexEncodeToString( - crypto.GetHMAC( - crypto.HashSHA512_384, - []byte(payload), - []byte(b.API.Credentials.Secret))), + AuthSig: crypto.HexEncodeToString(crypto.GetHMAC(crypto.HashSHA512_384, + []byte(payload), + []byte(b.API.Credentials.Secret))), AuthNonce: nonce, DeadManSwitch: 0, } - err := b.AuthenticatedWebsocketConn.SendJSONMessage(request) + err := b.Websocket.AuthConn.SendJSONMessage(request) if err != nil { b.Websocket.SetCanUseAuthenticatedEndpoints(false) return err @@ -954,7 +1048,8 @@ func (b *Bitfinex) WsAddSubscriptionChannel(chanID int, channel, pair string) { b.WebsocketSubdChannels[chanID] = chanInfo if b.Verbose { - log.Debugf(log.ExchangeSys, "%s Subscribed to Channel: %s Pair: %s ChannelID: %d\n", + log.Debugf(log.ExchangeSys, + "%s Subscribed to Channel: %s Pair: %s ChannelID: %d\n", b.Name, channel, pair, @@ -964,9 +1059,9 @@ func (b *Bitfinex) WsAddSubscriptionChannel(chanID int, channel, pair string) { // WsNewOrder authenticated new order request func (b *Bitfinex) WsNewOrder(data *WsNewOrderRequest) (string, error) { - data.CustomID = b.AuthenticatedWebsocketConn.GenerateMessageID(false) + data.CustomID = b.Websocket.AuthConn.GenerateMessageID(false) request := makeRequestInterface(wsOrderNew, data) - resp, err := b.AuthenticatedWebsocketConn.SendMessageReturnResponse(data.CustomID, request) + resp, err := b.Websocket.AuthConn.SendMessageReturnResponse(data.CustomID, request) if err != nil { return "", err } @@ -997,7 +1092,7 @@ func (b *Bitfinex) WsNewOrder(data *WsNewOrderRequest) (string, error) { // WsModifyOrder authenticated modify order request func (b *Bitfinex) WsModifyOrder(data *WsUpdateOrderRequest) error { request := makeRequestInterface(wsOrderUpdate, data) - resp, err := b.AuthenticatedWebsocketConn.SendMessageReturnResponse(data.OrderID, request) + resp, err := b.Websocket.AuthConn.SendMessageReturnResponse(data.OrderID, request) if err != nil { return err } @@ -1026,7 +1121,7 @@ func (b *Bitfinex) WsCancelMultiOrders(orderIDs []int64) error { OrderID: orderIDs, } request := makeRequestInterface(wsCancelMultipleOrders, cancel) - return b.AuthenticatedWebsocketConn.SendJSONMessage(request) + return b.Websocket.AuthConn.SendJSONMessage(request) } // WsCancelOrder authenticated cancel order request @@ -1035,7 +1130,7 @@ func (b *Bitfinex) WsCancelOrder(orderID int64) error { OrderID: orderID, } request := makeRequestInterface(wsOrderCancel, cancel) - resp, err := b.AuthenticatedWebsocketConn.SendMessageReturnResponse(orderID, request) + resp, err := b.Websocket.AuthConn.SendMessageReturnResponse(orderID, request) if err != nil { return err } @@ -1061,13 +1156,13 @@ func (b *Bitfinex) WsCancelOrder(orderID int64) error { func (b *Bitfinex) WsCancelAllOrders() error { cancelAll := WsCancelAllOrdersRequest{All: 1} request := makeRequestInterface(wsCancelMultipleOrders, cancelAll) - return b.AuthenticatedWebsocketConn.SendJSONMessage(request) + return b.Websocket.AuthConn.SendJSONMessage(request) } // WsNewOffer authenticated new offer request func (b *Bitfinex) WsNewOffer(data *WsNewOfferRequest) error { request := makeRequestInterface(wsFundingOrderNew, data) - return b.AuthenticatedWebsocketConn.SendJSONMessage(request) + return b.Websocket.AuthConn.SendJSONMessage(request) } // WsCancelOffer authenticated cancel offer request @@ -1076,7 +1171,7 @@ func (b *Bitfinex) WsCancelOffer(orderID int64) error { OrderID: orderID, } request := makeRequestInterface(wsFundingOrderCancel, cancel) - resp, err := b.AuthenticatedWebsocketConn.SendMessageReturnResponse(orderID, request) + resp, err := b.Websocket.AuthConn.SendMessageReturnResponse(orderID, request) if err != nil { return err } diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index 7b94995b..f31a94ec 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -57,17 +57,27 @@ func (b *Bitfinex) SetDefaults() { b.API.CredentialsValidator.RequiresKey = true b.API.CredentialsValidator.RequiresSecret = true - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - }, + fmt1 := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{Uppercase: true}, + ConfigFormat: ¤cy.PairFormat{Uppercase: true}, + } + + fmt2 := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{Uppercase: true}, + ConfigFormat: ¤cy.PairFormat{Uppercase: true, Delimiter: ":"}, + } + + err := b.StoreAssetPairFormat(asset.Spot, fmt1) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + err = b.StoreAssetPairFormat(asset.Margin, fmt2) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + err = b.StoreAssetPairFormat(asset.MarginFunding, fmt1) + if err != nil { + log.Errorln(log.ExchangeSys, err) } b.Features = exchange.Features{ @@ -111,7 +121,6 @@ func (b *Bitfinex) SetDefaults() { OrderbookFetching: true, AccountInfo: true, Subscribe: true, - Unsubscribe: true, AuthenticatedEndpoints: true, MessageCorrelation: true, DeadMansSwitch: true, @@ -155,7 +164,7 @@ func (b *Bitfinex) SetDefaults() { b.API.Endpoints.URLDefault = bitfinexAPIURLBase b.API.Endpoints.URL = b.API.Endpoints.URLDefault b.API.Endpoints.WebsocketURL = publicBitfinexWebsocketEndpoint - b.Websocket = wshandler.New() + b.Websocket = stream.New() b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout b.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -173,49 +182,41 @@ func (b *Bitfinex) Setup(exch *config.ExchangeConfig) error { return err } - err = b.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: publicBitfinexWebsocketEndpoint, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: b.WsConnect, - Subscriber: b.Subscribe, - UnSubscriber: b.Unsubscribe, - Features: &b.Features.Supports.WebsocketCapabilities, - }) + err = b.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: publicBitfinexWebsocketEndpoint, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: b.WsConnect, + Subscriber: b.Subscribe, + UnSubscriber: b.Unsubscribe, + GenerateSubscriptions: b.GenerateDefaultSubscriptions, + Features: &b.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + UpdateEntriesByID: true, + }) if err != nil { return err } - b.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: b.Websocket.GetWebsocketURL(), - ProxyURL: b.Websocket.GetProxyAddress(), - Verbose: b.Verbose, - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - b.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: authenticatedBitfinexWebsocketEndpoint, - ProxyURL: b.Websocket.GetProxyAddress(), - Verbose: b.Verbose, + err = b.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + URL: publicBitfinexWebsocketEndpoint, + }) + if err != nil { + return err } - b.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - false, - false, - true, - exch.Name) - return nil + return b.Websocket.SetupNewConnection(stream.ConnectionSetup{ + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + URL: authenticatedBitfinexWebsocketEndpoint, + Authenticated: true, + }) } // Start starts the Bitfinex go routine @@ -244,7 +245,9 @@ func (b *Bitfinex) Run() { err := b.UpdateTradablePairs(false) if err != nil { log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", b.Name, err) + "%s failed to update tradable pairs. Err: %s", + b.Name, + err) } } @@ -265,6 +268,13 @@ func (b *Bitfinex) FetchTradablePairs(a asset.Item) ([]string, error) { symbols = append(symbols, k[1:]) } case asset.Margin: + for k := range items { + if !strings.Contains(k, ":") { + continue + } + symbols = append(symbols, k[1:]) + } + case asset.MarginFunding: for k := range items { if !strings.HasPrefix(k, "f") { continue @@ -281,15 +291,19 @@ func (b *Bitfinex) FetchTradablePairs(a asset.Item) ([]string, error) { // UpdateTradablePairs updates the exchanges available pairs and stores // them in the exchanges config func (b *Bitfinex) UpdateTradablePairs(forceUpdate bool) error { - for i := range b.CurrencyPairs.AssetTypes { - pairs, err := b.FetchTradablePairs(b.CurrencyPairs.AssetTypes[i]) + assets := b.CurrencyPairs.GetAssetTypes() + for i := range assets { + pairs, err := b.FetchTradablePairs(assets[i]) if err != nil { return err } - err = b.UpdatePairs(currency.NewPairsFromStrings(pairs), - b.CurrencyPairs.AssetTypes[i], - false, - forceUpdate) + + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + err = b.UpdatePairs(p, assets[i], false, forceUpdate) if err != nil { return err } @@ -299,34 +313,40 @@ func (b *Bitfinex) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bitfinex) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - enabledPairs := b.GetEnabledPairs(assetType) + enabledPairs, err := b.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + tickerNew, err := b.GetTickerBatch() if err != nil { return nil, err } + for k, v := range tickerNew { - if strings.HasPrefix(k, "f") { - continue - } - pair := currency.NewPairFromString(k[1:]) // Remove prefix - if !enabledPairs.Contains(p, true) { - continue - } - tick := ticker.Price{ - Last: v.Last, - High: v.High, - Low: v.Low, - Bid: v.Bid, - Ask: v.Ask, - Volume: v.Volume, - Pair: pair, - } - err = ticker.ProcessTicker(b.Name, &tick, assetType) + pair, err := currency.NewPairFromString(k[1:]) // Remove prefix if err != nil { - log.Error(log.Ticker, err) + return nil, err + } + + if !enabledPairs.Contains(pair, true) { + continue + } + + err = ticker.ProcessTicker(&ticker.Price{ + Last: v.Last, + High: v.High, + Low: v.Low, + Bid: v.Bid, + Ask: v.Ask, + Volume: v.Volume, + Pair: pair, + AssetType: assetType, + ExchangeName: b.Name}) + if err != nil { + return nil, err } } - return ticker.GetTicker(b.Name, p, assetType) } @@ -354,7 +374,7 @@ func (b *Bitfinex) FetchOrderbook(p currency.Pair, assetType asset.Item) (*order func (b *Bitfinex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { b.appendOptionalDelimiter(&p) var prefix = "t" - if assetType == asset.Margin { + if assetType == asset.MarginFunding { prefix = "f" } @@ -459,11 +479,17 @@ func (b *Bitfinex) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) { if err != nil { return submitOrderResponse, err } + + fpair, err := b.FormatExchangeCurrency(o.Pair, o.AssetType) + if err != nil { + return submitOrderResponse, err + } + if b.Websocket.CanUseAuthenticatedWebsocketForWrapper() { submitOrderResponse.OrderID, err = b.WsNewOrder(&WsNewOrderRequest{ - CustomID: b.AuthenticatedWebsocketConn.GenerateMessageID(false), + CustomID: b.Websocket.AuthConn.GenerateMessageID(false), Type: o.Type.String(), - Symbol: b.FormatExchangeCurrency(o.Pair, asset.Spot).String(), + Symbol: fpair.String(), Amount: o.Amount, Price: o.Price, }) @@ -473,18 +499,22 @@ func (b *Bitfinex) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) { } else { var response Order isBuying := o.Side == order.Buy - b.appendOptionalDelimiter(&o.Pair) - response, err = b.NewOrder(o.Pair.String(), - o.Type.String(), + b.appendOptionalDelimiter(&fpair) + orderType := o.Type.Lower() + if o.AssetType == asset.Spot { + orderType = "exchange " + orderType + } + response, err = b.NewOrder(fpair.String(), + orderType, o.Amount, o.Price, - false, - isBuying) + isBuying, + false) if err != nil { return submitOrderResponse, err } - if response.OrderID > 0 { - submitOrderResponse.OrderID = strconv.FormatInt(response.OrderID, 10) + if response.ID > 0 { + submitOrderResponse.OrderID = strconv.FormatInt(response.ID, 10) } if response.RemainingAmount == 0 { submitOrderResponse.FullyMatched = true @@ -615,11 +645,6 @@ func (b *Bitfinex) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdra }, nil } -// GetWebsocket returns a pointer to the exchange websocket -func (b *Bitfinex) GetWebsocket() (*wshandler.Websocket, error) { - return b.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (b *Bitfinex) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !b.AllowAuthenticatedRequest() && // Todo check connection status @@ -639,23 +664,27 @@ func (b *Bitfinex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, for i := range resp { orderSide := order.Side(strings.ToUpper(resp[i].Side)) - timestamp, err := strconv.ParseInt(resp[i].Timestamp, 10, 64) + timestamp, err := strconv.ParseFloat(resp[i].Timestamp, 64) if err != nil { log.Warnf(log.ExchangeSys, "Unable to convert timestamp '%s', leaving blank", resp[i].Timestamp) } - orderDate := time.Unix(timestamp, 0) + + pair, err := currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } orderDetail := order.Detail{ Amount: resp[i].OriginalAmount, - Date: orderDate, + Date: time.Unix(int64(timestamp), 0), Exchange: b.Name, - ID: strconv.FormatInt(resp[i].OrderID, 10), + ID: strconv.FormatInt(resp[i].ID, 10), Side: orderSide, Price: resp[i].Price, RemainingAmount: resp[i].RemainingAmount, - Pair: currency.NewPairFromString(resp[i].Symbol), + Pair: pair, ExecutedAmount: resp[i].ExecutedAmount, } @@ -706,16 +735,21 @@ func (b *Bitfinex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, } orderDate := time.Unix(timestamp, 0) + pair, err := currency.NewPairFromString(resp[i].Symbol) + if err != nil { + return nil, err + } + orderDetail := order.Detail{ Amount: resp[i].OriginalAmount, Date: orderDate, Exchange: b.Name, - ID: strconv.FormatInt(resp[i].OrderID, 10), + ID: strconv.FormatInt(resp[i].ID, 10), Side: orderSide, Price: resp[i].Price, RemainingAmount: resp[i].RemainingAmount, ExecutedAmount: resp[i].ExecutedAmount, - Pair: currency.NewPairFromString(resp[i].Symbol), + Pair: pair, } switch { @@ -751,31 +785,6 @@ func (b *Bitfinex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *Bitfinex) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - for i := range channels { - b.appendOptionalDelimiter(&channels[i].Currency) - } - b.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *Bitfinex) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - for i := range channels { - b.appendOptionalDelimiter(&channels[i].Currency) - } - b.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *Bitfinex) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return b.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (b *Bitfinex) AuthenticateWebsocket() error { return b.WsSendAuth() @@ -822,7 +831,12 @@ func (b *Bitfinex) GetHistoricCandles(pair currency.Pair, a asset.Item, start, e return kline.Item{}, errors.New(kline.ErrRequestExceedsExchangeLimits) } - candles, err := b.GetCandles(b.fixCasing(pair, a), b.FormatExchangeKlineInterval(interval), + cf, err := b.fixCasing(pair, a) + if err != nil { + return kline.Item{}, err + } + + candles, err := b.GetCandles(cf, b.FormatExchangeKlineInterval(interval), start.Unix()*1000, end.Unix()*1000, b.Features.Enabled.Kline.ResultLimit, true) if err != nil { @@ -866,8 +880,13 @@ func (b *Bitfinex) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, } dates := kline.CalcDateRanges(start, end, interval, b.Features.Enabled.Kline.ResultLimit) + cf, err := b.fixCasing(pair, a) + if err != nil { + return kline.Item{}, err + } + for x := range dates { - candles, err := b.GetCandles(b.fixCasing(pair, a), b.FormatExchangeKlineInterval(interval), + candles, err := b.GetCandles(cf, b.FormatExchangeKlineInterval(interval), dates[x].Start.Unix()*1000, dates[x].End.Unix()*1000, b.Features.Enabled.Kline.ResultLimit, true) if err != nil { @@ -890,7 +909,7 @@ func (b *Bitfinex) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, return ret, nil } -func (b *Bitfinex) fixCasing(in currency.Pair, a asset.Item) string { +func (b *Bitfinex) fixCasing(in currency.Pair, a asset.Item) (string, error) { var checkString [2]byte if a == asset.Spot { checkString[0] = 't' @@ -900,13 +919,18 @@ func (b *Bitfinex) fixCasing(in currency.Pair, a asset.Item) string { checkString[1] = 'F' } + fmt, err := b.FormatExchangeCurrency(in, a) + if err != nil { + return "", err + } + y := in.Base.String() if (y[0] != checkString[0] && y[0] != checkString[1]) || (y[0] == checkString[1] && y[1] == checkString[1]) || in.Base == currency.TNB { - return string(checkString[0]) + b.FormatExchangeCurrency(in, a).Upper().String() + return string(checkString[0]) + fmt.Upper().String(), nil } - runes := []rune(b.FormatExchangeCurrency(in, a).Upper().String()) + runes := []rune(fmt.Upper().String()) runes[0] = unicode.ToLower(runes[0]) - return string(runes) + return string(runes), nil } diff --git a/exchanges/bitflyer/bitflyer_test.go b/exchanges/bitflyer/bitflyer_test.go index 01718bdd..bd735157 100644 --- a/exchanges/bitflyer/bitflyer_test.go +++ b/exchanges/bitflyer/bitflyer_test.go @@ -133,7 +133,10 @@ func TestGetExchangeStatus(t *testing.T) { func TestCheckFXString(t *testing.T) { t.Parallel() - p := currency.NewPairDelimiter("FXBTC_JPY", "_") + p, err := currency.NewPairDelimiter("FXBTC_JPY", "_") + if err != nil { + t.Fatal(err) + } p = b.CheckFXString(p) if p.Base.String() != "FX_BTC" { t.Error("Bitflyer - CheckFXString() error") @@ -144,15 +147,19 @@ func TestFetchTicker(t *testing.T) { t.Parallel() var p currency.Pair - currencies := b.GetAvailablePairs(asset.Spot) - for _, pair := range currencies { - if pair.String() == "FXBTC_JPY" { - p = pair + currencies, err := b.GetAvailablePairs(asset.Spot) + if err != nil { + t.Fatal(err) + } + + for i := range currencies { + if currencies[i].String() == "FXBTC_JPY" { + p = currencies[i] break } } - _, err := b.FetchTicker(p, asset.Spot) + _, err = b.FetchTicker(p, asset.Spot) if err != nil { t.Error("Bitflyer - FetchTicker() error", err) } diff --git a/exchanges/bitflyer/bitflyer_wrapper.go b/exchanges/bitflyer/bitflyer_wrapper.go index aff31ac4..038b42a0 100644 --- a/exchanges/bitflyer/bitflyer_wrapper.go +++ b/exchanges/bitflyer/bitflyer_wrapper.go @@ -17,7 +17,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -53,20 +52,20 @@ func (b *Bitflyer) SetDefaults() { b.API.CredentialsValidator.RequiresKey = true b.API.CredentialsValidator.RequiresSecret = true - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Futures, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{ + Delimiter: currency.UnderscoreDelimiter, + Uppercase: true, + } + configFmt := ¤cy.PairFormat{ + Delimiter: currency.UnderscoreDelimiter, + Uppercase: true, + } + err := b.SetGlobalPairsManager(requestFmt, + configFmt, + asset.Spot, + asset.Futures) + if err != nil { + log.Errorln(log.ExchangeSys, err) } b.Features = exchange.Features{ @@ -105,7 +104,6 @@ func (b *Bitflyer) Setup(exch *config.ExchangeConfig) error { b.SetEnabled(false) return nil } - return b.SetupDefaults(exch) } @@ -141,6 +139,11 @@ func (b *Bitflyer) FetchTradablePairs(assetType asset.Item) ([]string, error) { return nil, err } + format, err := b.GetPairFormat(assetType, false) + if err != nil { + return nil, err + } + var products []string for i := range pairs { if pairs[i].Alias != "" && assetType == asset.Futures { @@ -148,7 +151,7 @@ func (b *Bitflyer) FetchTradablePairs(assetType asset.Item) ([]string, error) { } else if pairs[i].Alias == "" && assetType == asset.Spot && strings.Contains(pairs[i].ProductCode, - b.GetPairFormat(assetType, false).Delimiter) { + format.Delimiter) { products = append(products, pairs[i].ProductCode) } } @@ -158,17 +161,19 @@ func (b *Bitflyer) FetchTradablePairs(assetType asset.Item) ([]string, error) { // UpdateTradablePairs updates the exchanges available pairs and stores // them in the exchanges config func (b *Bitflyer) UpdateTradablePairs(forceUpdate bool) error { - for x := range b.CurrencyPairs.AssetTypes { - a := b.CurrencyPairs.AssetTypes[x] - pairs, err := b.FetchTradablePairs(a) + assets := b.CurrencyPairs.GetAssetTypes() + for x := range assets { + pairs, err := b.FetchTradablePairs(assets[x]) if err != nil { return err } - err = b.UpdatePairs(currency.NewPairsFromStrings(pairs), - a, - false, - forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + err = b.UpdatePairs(p, assets[x], false, forceUpdate) if err != nil { return err } @@ -178,23 +183,21 @@ func (b *Bitflyer) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bitflyer) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) - - p = b.CheckFXString(p) - - tickerNew, err := b.GetTicker(p.String()) + tickerNew, err := b.GetTicker(b.CheckFXString(p).String()) if err != nil { - return tickerPrice, err + return nil, err } - tickerPrice.Pair = p - tickerPrice.Ask = tickerNew.BestAsk - tickerPrice.Bid = tickerNew.BestBid - tickerPrice.Last = tickerNew.Last - tickerPrice.Volume = tickerNew.Volume - err = ticker.ProcessTicker(b.Name, tickerPrice, assetType) + err = ticker.ProcessTicker(&ticker.Price{ + Pair: p, + Ask: tickerNew.BestAsk, + Bid: tickerNew.BestBid, + Last: tickerNew.Last, + Volume: tickerNew.Volume, + ExchangeName: b.Name, + AssetType: assetType}) if err != nil { - return tickerPrice, err + return nil, err } return ticker.GetTicker(b.Name, p, assetType) @@ -231,9 +234,7 @@ func (b *Bitflyer) FetchOrderbook(p currency.Pair, assetType asset.Item) (*order func (b *Bitflyer) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { orderBook := new(orderbook.Base) - p = b.CheckFXString(p) - - orderbookNew, err := b.GetOrderBook(p.String()) + orderbookNew, err := b.GetOrderBook(b.CheckFXString(p).String()) if err != nil { return orderBook, err } @@ -337,11 +338,6 @@ func (b *Bitflyer) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdra return nil, common.ErrNotYetImplemented } -// GetWebsocket returns a pointer to the exchange websocket -func (b *Bitflyer) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrNotYetImplemented -} - // GetActiveOrders retrieves any orders that are active/open func (b *Bitflyer) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) { return nil, common.ErrNotYetImplemented @@ -362,28 +358,6 @@ func (b *Bitflyer) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error return b.GetFee(feeBuilder) } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *Bitflyer) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *Bitflyer) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *Bitflyer) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (b *Bitflyer) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (b *Bitflyer) ValidateCredentials() error { diff --git a/exchanges/bithumb/bithumb_test.go b/exchanges/bithumb/bithumb_test.go index c27e3755..e763fc93 100644 --- a/exchanges/bithumb/bithumb_test.go +++ b/exchanges/bithumb/bithumb_test.go @@ -207,6 +207,20 @@ func TestMarketSellOrder(t *testing.T) { } } +func TestUpdateTicker(t *testing.T) { + cp := currency.NewPair(currency.QTUM, currency.KRW) + _, err := b.UpdateTicker(cp, asset.Spot) + if err != nil { + t.Fatal(err) + } + + cp = currency.NewPair(currency.DASH, currency.KRW) + _, err = b.UpdateTicker(cp, asset.Spot) + if err != nil { + t.Fatal(err) + } +} + func setFeeBuilder() *exchange.FeeBuilder { return &exchange.FeeBuilder{ Amount: 1, @@ -436,8 +450,11 @@ func TestGetAccountInfo(t *testing.T) { func TestModifyOrder(t *testing.T) { t.Parallel() - curr := currency.NewPairFromString("BTCUSD") - _, err := b.ModifyOrder(&order.Modify{ + curr, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + _, err = b.ModifyOrder(&order.Modify{ ID: "1337", Price: 100, Amount: 1000, @@ -535,18 +552,24 @@ func TestGetCandleStick(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTC_KRW") + currencyPair, err := currency.NewPairFromString("BTC_KRW") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 24) - _, err := b.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneDay) + _, err = b.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneDay) if err != nil { t.Fatal(err) } } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("BTC_KRW") + currencyPair, err := currency.NewPairFromString("BTC_KRW") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 24) - _, err := b.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneDay) + _, err = b.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneDay) if err != nil { t.Fatal(err) } diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 3baeab20..32701678 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -20,7 +20,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,20 +55,11 @@ func (b *Bithumb) SetDefaults() { b.API.CredentialsValidator.RequiresKey = true b.API.CredentialsValidator.RequiresSecret = true - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "_", - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Index: "KRW", - }, + requestFmt := ¤cy.PairFormat{Uppercase: true, Delimiter: currency.UnderscoreDelimiter} + configFmt := ¤cy.PairFormat{Uppercase: true, Index: "KRW"} + err := b.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } b.Features = exchange.Features{ @@ -135,7 +125,6 @@ func (b *Bithumb) Setup(exch *config.ExchangeConfig) error { b.SetEnabled(false) return nil } - return b.SetupDefaults(exch) } @@ -186,34 +175,46 @@ func (b *Bithumb) UpdateTradablePairs(forceUpdate bool) error { return err } - return b.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + return b.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (b *Bithumb) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) tickers, err := b.GetAllTickers() if err != nil { - return tickerPrice, err + return nil, err } - pairs := b.GetEnabledPairs(assetType) + pairs, err := b.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + for i := range pairs { curr := pairs[i].Base.String() t, ok := tickers[curr] if !ok { - continue + return nil, + fmt.Errorf("enabled pair %s [%s] not found in returned ticker map %v", + pairs[i], pairs, tickers) } - tp := ticker.Price{ - High: t.MaxPrice, - Low: t.MinPrice, - Volume: t.UnitsTraded24Hr, - Open: t.OpeningPrice, - Close: t.ClosingPrice, - Pair: pairs[i], - } - err = ticker.ProcessTicker(b.Name, &tp, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + High: t.MaxPrice, + Low: t.MinPrice, + Volume: t.UnitsTraded24Hr, + Open: t.OpeningPrice, + Close: t.ClosingPrice, + Pair: pairs[i], + ExchangeName: b.Name, + AssetType: assetType, + }) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } return ticker.GetTicker(b.Name, p, assetType) @@ -398,7 +399,11 @@ func (b *Bithumb) CancelAllOrders(orderCancellation *order.Cancel) (order.Cancel } var allOrders []OrderData - currs := b.GetEnabledPairs(asset.Spot) + currs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } + for i := range currs { orders, err := b.GetOrders("", orderCancellation.Side.String(), @@ -484,11 +489,6 @@ func (b *Bithumb) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (b *Bithumb) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrFunctionNotSupported -} - // GetFeeByType returns an estimate of fee based on type of transaction func (b *Bithumb) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !b.AllowAuthenticatedRequest() && // Todo check connection status @@ -506,6 +506,11 @@ func (b *Bithumb) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, return nil, err } + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + for i := range resp.Data { if resp.Data[i].Status != "placed" { continue @@ -522,7 +527,7 @@ func (b *Bithumb) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, Status: order.Active, Pair: currency.NewPairWithDelimiter(resp.Data[i].OrderCurrency, resp.Data[i].PaymentCurrency, - b.GetPairFormat(asset.Spot, false).Delimiter), + format.Delimiter), } if resp.Data[i].Type == "bid" { @@ -549,6 +554,11 @@ func (b *Bithumb) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return nil, err } + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + for i := range resp.Data { if resp.Data[i].Status == "placed" { continue @@ -564,7 +574,7 @@ func (b *Bithumb) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, RemainingAmount: resp.Data[i].UnitsRemaining, Pair: currency.NewPairWithDelimiter(resp.Data[i].OrderCurrency, resp.Data[i].PaymentCurrency, - b.GetPairFormat(asset.Spot, false).Delimiter), + format.Delimiter), } if resp.Data[i].Type == "bid" { @@ -582,28 +592,6 @@ func (b *Bithumb) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *Bithumb) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *Bithumb) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *Bithumb) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (b *Bithumb) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (b *Bithumb) ValidateCredentials() error { @@ -624,7 +612,13 @@ func (b *Bithumb) GetHistoricCandles(pair currency.Pair, a asset.Item, start, en } } - candle, err := b.GetCandleStick(b.FormatExchangeCurrency(pair, a).String(), b.FormatExchangeKlineInterval(interval)) + formattedPair, err := b.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + + candle, err := b.GetCandleStick(formattedPair.String(), + b.FormatExchangeKlineInterval(interval)) if err != nil { return kline.Item{}, err } diff --git a/exchanges/bitmex/bitmex.go b/exchanges/bitmex/bitmex.go index c2cdffbf..fe475b80 100644 --- a/exchanges/bitmex/bitmex.go +++ b/exchanges/bitmex/bitmex.go @@ -14,13 +14,11 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) // Bitmex is the overarching type across this package type Bitmex struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } const ( diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index 82baab39..69fbb020 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -16,7 +16,7 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -45,13 +45,11 @@ func TestMain(m *testing.M) { bitmexConfig.API.AuthenticatedWebsocketSupport = true bitmexConfig.API.Credentials.Key = apiKey bitmexConfig.API.Credentials.Secret = apiSecret - + b.Websocket = sharedtestvalues.NewTestWebsocket() err = b.Setup(bitmexConfig) if err != nil { log.Fatal("Bitmex setup error", err) } - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -678,17 +676,10 @@ func TestGetDepositAddress(t *testing.T) { // TestWsAuth dials websocket, sends login request. func TestWsAuth(t *testing.T) { if !b.Websocket.IsEnabled() && !b.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) - } - b.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: b.Websocket.GetWebsocketURL(), - Verbose: b.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + t.Skip(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := b.WebsocketConn.Dial(&dialer, http.Header{}) + err := b.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } @@ -710,6 +701,13 @@ func TestWsAuth(t *testing.T) { timer.Stop() } +func TestUpdateTradablePairs(t *testing.T) { + err := b.UpdateTradablePairs(true) + if err != nil { + t.Fatal(err) + } +} + func TestWsPositionUpdate(t *testing.T) { pressXToJSON := []byte(`{"table":"position", "action":"update", @@ -800,7 +798,6 @@ func TestWSPositionUpdateHandling(t *testing.T) { } func TestWSOrderbookHandling(t *testing.T) { - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() pressXToJSON := []byte(`{ "table":"orderBookL2_25", "keys":["symbol","id","side"], @@ -871,7 +868,6 @@ func TestWSOrderbookHandling(t *testing.T) { } func TestWSDeleveragePositionUpdateHandling(t *testing.T) { - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() pressXToJSON := []byte(`{"table":"position", "action":"update", "data":[{ diff --git a/exchanges/bitmex/bitmex_websocket.go b/exchanges/bitmex/bitmex_websocket.go index 0b3789a2..59e0ac17 100644 --- a/exchanges/bitmex/bitmex_websocket.go +++ b/exchanges/bitmex/bitmex_websocket.go @@ -16,8 +16,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -67,67 +67,72 @@ const ( // WsConnect initiates a new websocket connection func (b *Bitmex) WsConnect() error { if !b.Websocket.IsEnabled() || !b.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := b.WebsocketConn.Dial(&dialer, http.Header{}) + err := b.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } - p, err := b.WebsocketConn.ReadMessage() - if err != nil { - b.Websocket.ReadMessageErrors <- err - return err + resp := b.Websocket.Conn.ReadMessage() + if resp.Raw == nil { + return errors.New("connection closed") } - b.Websocket.TrafficAlert <- struct{}{} var welcomeResp WebsocketWelcome - err = json.Unmarshal(p.Raw, &welcomeResp) + err = json.Unmarshal(resp.Raw, &welcomeResp) if err != nil { return err } if b.Verbose { - log.Debugf(log.ExchangeSys, "Successfully connected to Bitmex %s at time: %s Limit: %d", + log.Debugf(log.ExchangeSys, + "Successfully connected to Bitmex %s at time: %s Limit: %d", welcomeResp.Info, welcomeResp.Timestamp, welcomeResp.Limit.Remaining) } go b.wsReadData() - b.GenerateDefaultSubscriptions() + subs, err := b.GenerateDefaultSubscriptions() + if err != nil { + return err + } + + err = b.Websocket.SubscribeToChannels(subs) + if err != nil { + return err + } + err = b.websocketSendAuth() if err != nil { - log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", b.Name, err) + log.Errorf(log.ExchangeSys, + "%v - authentication failed: %v\n", + b.Name, + err) + } else { + authsubs, err := b.GenerateAuthenticatedSubscriptions() + if err != nil { + return err + } + return b.Websocket.SubscribeToChannels(authsubs) } - b.GenerateAuthenticatedSubscriptions() return nil } // wsReadData receives and passes on websocket messages for processing func (b *Bitmex) wsReadData() { b.Websocket.Wg.Add(1) - - defer func() { - b.Websocket.Wg.Done() - }() + defer b.Websocket.Wg.Done() for { - select { - case <-b.Websocket.ShutdownC: + resp := b.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - - default: - resp, err := b.WebsocketConn.ReadMessage() - if err != nil { - b.Websocket.DataHandler <- err - return - } - b.Websocket.TrafficAlert <- struct{}{} - err = b.wsHandleData(resp.Raw) - if err != nil { - b.Websocket.DataHandler <- err - } + } + err := b.wsHandleData(resp.Raw) + if err != nil { + b.Websocket.DataHandler <- err } } } @@ -188,7 +193,12 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { if len(orderbooks.Data) == 0 { return fmt.Errorf("%s - Empty orderbook data received: %s", b.Name, respRaw) } - p := currency.NewPairFromString(orderbooks.Data[0].Symbol) + var p currency.Pair + p, err = currency.NewPairFromString(orderbooks.Data[0].Symbol) + if err != nil { + return err + } + var a asset.Item a, err = b.GetPairAssetType(p) if err != nil { @@ -210,13 +220,14 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { return err } - if trades.Action == bitmexActionInitialData { - return nil - } - for i := range trades.Data { + var p currency.Pair + p, err = currency.NewPairFromString(trades.Data[i].Symbol) + if err != nil { + return err + } + var a asset.Item - p := currency.NewPairFromString(trades.Data[i].Symbol) a, err = b.GetPairAssetType(p) if err != nil { return err @@ -230,7 +241,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { } } - b.Websocket.DataHandler <- wshandler.TradeData{ + b.Websocket.DataHandler <- stream.TradeData{ Timestamp: trades.Data[i].Timestamp, Price: trades.Data[i].Price, Amount: float64(trades.Data[i].Size), @@ -271,7 +282,12 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { } for i := range response.Data { - p := currency.NewPairFromString(response.Data[i].Symbol) + var p currency.Pair + p, err = currency.NewPairFromString(response.Data[i].Symbol) + if err != nil { + return err + } + var a asset.Item a, err = b.GetPairAssetType(p) if err != nil { @@ -459,7 +475,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { } b.Websocket.DataHandler <- response default: - b.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: b.Name + wshandler.UnhandledMessage + string(respRaw)} + b.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: b.Name + stream.UnhandledMessage + string(respRaw)} return nil } } @@ -467,7 +483,7 @@ func (b *Bitmex) wsHandleData(respRaw []byte) error { } // ProcessOrderbook processes orderbook updates -func (b *Bitmex) processOrderbook(data []OrderBookL2, action string, currencyPair currency.Pair, assetType asset.Item) error { +func (b *Bitmex) processOrderbook(data []OrderBookL2, action string, p currency.Pair, a asset.Item) error { if len(data) < 1 { return errors.New("bitmex_websocket.go error - no orderbook data") } @@ -490,8 +506,8 @@ func (b *Bitmex) processOrderbook(data []OrderBookL2, action string, currencyPai ID: data[i].ID, }) } - newOrderBook.AssetType = assetType - newOrderBook.Pair = currencyPair + newOrderBook.AssetType = a + newOrderBook.Pair = p newOrderBook.ExchangeName = b.Name err := b.Websocket.Orderbook.LoadSnapshot(&newOrderBook) @@ -499,11 +515,6 @@ func (b *Bitmex) processOrderbook(data []OrderBookL2, action string, currencyPai return fmt.Errorf("bitmex_websocket.go process orderbook error - %s", err) } - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: currencyPair, - Asset: assetType, - Exchange: b.Name, - } default: var asks, bids []orderbook.Item for i := range data { @@ -520,40 +531,42 @@ func (b *Bitmex) processOrderbook(data []OrderBookL2, action string, currencyPai }) } - err := b.Websocket.Orderbook.Update(&wsorderbook.WebsocketOrderbookUpdate{ + err := b.Websocket.Orderbook.Update(&buffer.Update{ Bids: bids, Asks: asks, - Pair: currencyPair, - Asset: assetType, + Pair: p, + Asset: a, Action: action, }) if err != nil { return err } - - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: currencyPair, - Asset: assetType, - Exchange: b.Name, - } } return nil } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (b *Bitmex) GenerateDefaultSubscriptions() { +func (b *Bitmex) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { assets := b.GetAssetTypes() var allPairs currency.Pairs - + var associatedAssets []asset.Item for x := range assets { - contracts := b.GetEnabledPairs(assets[x]) + contracts, err := b.GetEnabledPairs(assets[x]) + if err != nil { + return nil, err + } for y := range contracts { allPairs = allPairs.Add(contracts[y]) + associatedAssets = append(associatedAssets, assets[x]) } } + if len(allPairs) != len(associatedAssets) { + return nil, fmt.Errorf("%s generate default subscriptions: pair and asset type len mismatch", b.Name) + } + channels := []string{bitmexWSOrderbookL2, bitmexWSTrade} - subscriptions := []wshandler.WebsocketChannelSubscription{ + subscriptions := []stream.ChannelSubscription{ { Channel: bitmexWSAnnouncement, }, @@ -561,25 +574,29 @@ func (b *Bitmex) GenerateDefaultSubscriptions() { for i := range channels { for j := range allPairs { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channels[i] + ":" + allPairs[j].String(), Currency: allPairs[j], + Asset: associatedAssets[j], }) } } - b.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // GenerateAuthenticatedSubscriptions Adds authenticated subscriptions to websocket to be handled by ManageSubscriptions() -func (b *Bitmex) GenerateAuthenticatedSubscriptions() { +func (b *Bitmex) GenerateAuthenticatedSubscriptions() ([]stream.ChannelSubscription, error) { if !b.Websocket.CanUseAuthenticatedEndpoints() { - return + return nil, nil + } + contracts, err := b.GetEnabledPairs(asset.PerpetualContract) + if err != nil { + return nil, err } - contracts := b.GetEnabledPairs(asset.PerpetualContract) channels := []string{bitmexWSExecution, bitmexWSPosition, } - subscriptions := []wshandler.WebsocketChannelSubscription{ + subscriptions := []stream.ChannelSubscription{ { Channel: bitmexWSAffiliate, }, @@ -601,31 +618,48 @@ func (b *Bitmex) GenerateAuthenticatedSubscriptions() { } for i := range channels { for j := range contracts { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channels[i] + ":" + contracts[j].String(), Currency: contracts[j], + Asset: asset.PerpetualContract, }) } } - b.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe subscribes to a websocket channel -func (b *Bitmex) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { +func (b *Bitmex) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { var subscriber WebsocketRequest subscriber.Command = "subscribe" - subscriber.Arguments = append(subscriber.Arguments, channelToSubscribe.Channel) - return b.WebsocketConn.SendJSONMessage(subscriber) + + for i := range channelsToSubscribe { + subscriber.Arguments = append(subscriber.Arguments, + channelsToSubscribe[i].Channel) + } + err := b.Websocket.Conn.SendJSONMessage(subscriber) + if err != nil { + return err + } + b.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe...) + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (b *Bitmex) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - var subscriber WebsocketRequest - subscriber.Command = "unsubscribe" - subscriber.Arguments = append(subscriber.Arguments, - channelToSubscribe.Params["args"], - channelToSubscribe.Channel+":"+channelToSubscribe.Currency.String()) - return b.WebsocketConn.SendJSONMessage(subscriber) +func (b *Bitmex) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var unsubscriber WebsocketRequest + unsubscriber.Command = "unsubscribe" + + for i := range channelsToUnsubscribe { + unsubscriber.Arguments = append(unsubscriber.Arguments, + channelsToUnsubscribe[i].Channel) + } + err := b.Websocket.Conn.SendJSONMessage(unsubscriber) + if err != nil { + return err + } + b.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe...) + return nil } // WebsocketSendAuth sends an authenticated subscription @@ -645,7 +679,7 @@ func (b *Bitmex) websocketSendAuth() error { sendAuth.Command = "authKeyExpires" sendAuth.Arguments = append(sendAuth.Arguments, b.API.Credentials.Key, timestamp, signature) - err := b.WebsocketConn.SendJSONMessage(sendAuth) + err := b.Websocket.Conn.SendJSONMessage(sendAuth) if err != nil { b.Websocket.SetCanUseAuthenticatedEndpoints(false) return err diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index 68a38f93..b5c6ad6d 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -18,8 +18,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -55,41 +55,17 @@ func (b *Bitmex) SetDefaults() { b.API.CredentialsValidator.RequiresKey = true b.API.CredentialsValidator.RequiresSecret = true - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.PerpetualContract, - asset.Futures, - asset.DownsideProfitContract, - asset.UpsideProfitContract, - }, + requestFmt := ¤cy.PairFormat{Uppercase: true} + configFmt := ¤cy.PairFormat{Uppercase: true} + err := b.SetGlobalPairsManager(requestFmt, + configFmt, + asset.PerpetualContract, + asset.Futures, + asset.Index) + if err != nil { + log.Errorln(log.ExchangeSys, err) } - // Same format used for perpetual contracts and futures - fmt1 := currency.PairStore{ - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - } - b.CurrencyPairs.Store(asset.PerpetualContract, fmt1) - b.CurrencyPairs.Store(asset.Futures, fmt1) - - // Upside and Downside profit contracts use the same format - fmt2 := currency.PairStore{ - RequestFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - }, - } - b.CurrencyPairs.Store(asset.DownsideProfitContract, fmt2) - b.CurrencyPairs.Store(asset.UpsideProfitContract, fmt2) - b.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ REST: true, @@ -144,7 +120,7 @@ func (b *Bitmex) SetDefaults() { b.API.Endpoints.URLDefault = bitmexAPIURL b.API.Endpoints.URL = b.API.Endpoints.URLDefault b.API.Endpoints.WebsocketURL = bitmexWSURL - b.Websocket = wshandler.New() + b.Websocket = stream.New() b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout b.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -162,41 +138,30 @@ func (b *Bitmex) Setup(exch *config.ExchangeConfig) error { return err } - err = b.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: bitmexWSURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: b.WsConnect, - Subscriber: b.Subscribe, - UnSubscriber: b.Unsubscribe, - Features: &b.Features.Supports.WebsocketCapabilities, - }) + err = b.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: bitmexWSURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: b.WsConnect, + Subscriber: b.Subscribe, + UnSubscriber: b.Unsubscribe, + GenerateSubscriptions: b.GenerateDefaultSubscriptions, + Features: &b.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + UpdateEntriesByID: true, + }) if err != nil { return err } - b.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: b.Websocket.GetWebsocketURL(), - ProxyURL: b.Websocket.GetProxyAddress(), - Verbose: b.Verbose, + return b.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - b.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - false, - false, - true, - exch.Name) - return nil + }) } // Start starts the Bitmex go routine @@ -211,7 +176,11 @@ func (b *Bitmex) Start(wg *sync.WaitGroup) { // Run implements the Bitmex wrapper func (b *Bitmex) Run() { if b.Verbose { - log.Debugf(log.ExchangeSys, "%s Websocket: %s. (url: %s).\n", b.Name, common.IsEnabled(b.Websocket.IsEnabled()), b.API.Endpoints.WebsocketURL) + log.Debugf(log.ExchangeSys, + "%s Websocket: %s. (url: %s).\n", + b.Name, + common.IsEnabled(b.Websocket.IsEnabled()), + b.API.Endpoints.WebsocketURL) b.PrintEnabledPairs() } @@ -226,8 +195,8 @@ func (b *Bitmex) Run() { } // FetchTradablePairs returns a list of the exchanges tradable pairs -func (b *Bitmex) FetchTradablePairs(_ asset.Item) ([]string, error) { - marketInfo, err := b.GetActiveInstruments(&GenericRequestParams{}) +func (b *Bitmex) FetchTradablePairs(asset asset.Item) ([]string, error) { + marketInfo, err := b.GetActiveAndIndexInstruments() if err != nil { return nil, err } @@ -248,72 +217,78 @@ func (b *Bitmex) UpdateTradablePairs(forceUpdate bool) error { return err } - var assetPairs []string - for x := range b.CurrencyPairs.AssetTypes { - switch b.CurrencyPairs.AssetTypes[x] { - case asset.PerpetualContract: - for y := range pairs { - if strings.Contains(pairs[y], "USD") { - assetPairs = append(assetPairs, pairs[y]) - } - } - case asset.Futures: - for y := range pairs { - if strings.Contains(pairs[y], "20") { - assetPairs = append(assetPairs, pairs[y]) - } - } - case asset.DownsideProfitContract: - for y := range pairs { - if strings.Contains(pairs[y], "_D") { - assetPairs = append(assetPairs, pairs[y]) - } - } - case asset.UpsideProfitContract: - for y := range pairs { - if strings.Contains(pairs[y], "_U") { - assetPairs = append(assetPairs, pairs[y]) - } - } + // Zerovalue current list which will remove old asset pairs when contract + // types expire or become obsolete + var assetPairs = map[asset.Item][]string{ + asset.Index: {}, + asset.PerpetualContract: {}, + asset.Futures: {}, + } + + for x := range pairs { + if strings.Contains(pairs[x], ".") { + assetPairs[asset.Index] = append(assetPairs[asset.Index], pairs[x]) + continue } - err = b.UpdatePairs(currency.NewPairsFromStrings(assetPairs), b.CurrencyPairs.AssetTypes[x], false, false) - if err != nil { - log.Warnf(log.ExchangeSys, "%s failed to update available pairs. Err: %v", b.Name, err) + if strings.Contains(pairs[x], "USD") { + assetPairs[asset.PerpetualContract] = append(assetPairs[asset.PerpetualContract], + pairs[x]) + continue } - assetPairs = nil + + assetPairs[asset.Futures] = append(assetPairs[asset.Futures], pairs[x]) } + + for a, values := range assetPairs { + p, err := currency.NewPairsFromStrings(values) + if err != nil { + return err + } + + err = b.UpdatePairs(p, a, false, false) + if err != nil { + log.Warnf(log.ExchangeSys, + "%s failed to update available pairs. Err: %v", + b.Name, + err) + } + } + return nil } // UpdateTicker updates and returns the ticker for a currency pair func (b *Bitmex) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) - tick, err := b.GetActiveInstruments(&GenericRequestParams{}) + tick, err := b.GetActiveAndIndexInstruments() if err != nil { - return tickerPrice, err + return nil, err } - pairs := b.GetEnabledPairs(assetType) - for i := range pairs { - for j := range tick { - if !pairs[i].Equal(tick[j].Symbol) { - continue - } - tickerPrice = &ticker.Price{ - Last: tick[j].LastPrice, - High: tick[j].HighPrice, - Low: tick[j].LowPrice, - Bid: tick[j].BidPrice, - Ask: tick[j].AskPrice, - Volume: tick[j].Volume24h, - Close: tick[j].PrevClosePrice, - Pair: tick[j].Symbol, - LastUpdated: tick[j].Timestamp, - } - err = ticker.ProcessTicker(b.Name, tickerPrice, assetType) - if err != nil { - log.Error(log.Ticker, err) - } + + pairs, err := b.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + + for j := range tick { + if !pairs.Contains(tick[j].Symbol, true) { + continue + } + + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick[j].LastPrice, + High: tick[j].HighPrice, + Low: tick[j].LowPrice, + Bid: tick[j].BidPrice, + Ask: tick[j].AskPrice, + Volume: tick[j].Volume24h, + Close: tick[j].PrevClosePrice, + Pair: tick[j].Symbol, + LastUpdated: tick[j].Timestamp, + ExchangeName: b.Name, + AssetType: assetType}) + if err != nil { + return nil, err } } return ticker.GetTicker(b.Name, p, assetType) @@ -339,25 +314,34 @@ func (b *Bitmex) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbo // UpdateOrderbook updates and returns the orderbook for a currency pair func (b *Bitmex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - - orderbookNew, err := b.GetOrderbook(OrderBookGetL2Params{ - Symbol: b.FormatExchangeCurrency(p, assetType).String(), - Depth: 500}) - if err != nil { - return orderBook, err + if assetType == asset.Index { + return nil, common.ErrFunctionNotSupported } - for _, ob := range orderbookNew { - if strings.EqualFold(ob.Side, order.Sell.String()) { - orderBook.Asks = append(orderBook.Asks, - orderbook.Item{Amount: float64(ob.Size), Price: ob.Price}) + fpair, err := b.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + + orderbookNew, err := b.GetOrderbook(OrderBookGetL2Params{ + Symbol: fpair.String(), + Depth: 500}) + if err != nil { + return nil, err + } + + orderBook := new(orderbook.Base) + for i := range orderbookNew { + if strings.EqualFold(orderbookNew[i].Side, order.Sell.String()) { + orderBook.Asks = append(orderBook.Asks, orderbook.Item{ + Amount: float64(orderbookNew[i].Size), + Price: orderbookNew[i].Price}) continue } - if strings.EqualFold(ob.Side, order.Buy.String()) { - orderBook.Bids = append(orderBook.Bids, - orderbook.Item{Amount: float64(ob.Size), Price: ob.Price}) - continue + if strings.EqualFold(orderbookNew[i].Side, order.Buy.String()) { + orderBook.Bids = append(orderBook.Bids, orderbook.Item{ + Amount: float64(orderbookNew[i].Size), + Price: orderbookNew[i].Price}) } } @@ -561,11 +545,6 @@ func (b *Bitmex) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw. return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (b *Bitmex) GetWebsocket() (*wshandler.Websocket, error) { - return b.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (b *Bitmex) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !b.AllowAuthenticatedRequest() && // Todo check connection status @@ -587,6 +566,11 @@ func (b *Bitmex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e return nil, err } + format, err := b.GetPairFormat(asset.PerpetualContract, false) + if err != nil { + return nil, err + } + for i := range resp { orderSide := orderSideMap[resp[i].Side] orderType := orderTypeMap[resp[i].OrdType] @@ -604,7 +588,7 @@ func (b *Bitmex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e Status: order.Status(resp[i].OrdStatus), Pair: currency.NewPairWithDelimiter(resp[i].Symbol, resp[i].SettlCurrency, - b.GetPairFormat(asset.PerpetualContract, false).Delimiter), + format.Delimiter), } orders = append(orders, orderDetail) @@ -628,6 +612,11 @@ func (b *Bitmex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e return nil, err } + format, err := b.GetPairFormat(asset.PerpetualContract, false) + if err != nil { + return nil, err + } + for i := range resp { orderSide := orderSideMap[resp[i].Side] orderType := orderTypeMap[resp[i].OrdType] @@ -645,7 +634,7 @@ func (b *Bitmex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e Status: order.Status(resp[i].OrdStatus), Pair: currency.NewPairWithDelimiter(resp[i].Symbol, resp[i].SettlCurrency, - b.GetPairFormat(asset.PerpetualContract, false).Delimiter), + format.Delimiter), } orders = append(orders, orderDetail) @@ -658,25 +647,6 @@ func (b *Bitmex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *Bitmex) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - b.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *Bitmex) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - b.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *Bitmex) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return b.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (b *Bitmex) AuthenticateWebsocket() error { return b.websocketSendAuth() diff --git a/exchanges/bitstamp/bitstamp.go b/exchanges/bitstamp/bitstamp.go index 384a61f1..ca88a128 100644 --- a/exchanges/bitstamp/bitstamp.go +++ b/exchanges/bitstamp/bitstamp.go @@ -18,7 +18,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -64,7 +63,6 @@ const ( // Bitstamp is the overarching type across the bitstamp package type Bitstamp struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } // GetFee returns an estimate of fee based on type of transaction diff --git a/exchanges/bitstamp/bitstamp_live_test.go b/exchanges/bitstamp/bitstamp_live_test.go index 5c6cebb0..96150d1e 100644 --- a/exchanges/bitstamp/bitstamp_live_test.go +++ b/exchanges/bitstamp/bitstamp_live_test.go @@ -30,12 +30,11 @@ func TestMain(m *testing.M) { bitstampConfig.API.Credentials.Secret = apiSecret bitstampConfig.API.Credentials.ClientID = customerID b.SetDefaults() + b.Websocket = sharedtestvalues.NewTestWebsocket() err = b.Setup(bitstampConfig) if err != nil { log.Fatal("Bitstamp setup error", err) } - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() log.Printf(sharedtestvalues.LiveTesting, b.Name, b.API.Endpoints.URL) os.Exit(m.Run()) } diff --git a/exchanges/bitstamp/bitstamp_mock_test.go b/exchanges/bitstamp/bitstamp_mock_test.go index 136f8a85..eabbfc88 100644 --- a/exchanges/bitstamp/bitstamp_mock_test.go +++ b/exchanges/bitstamp/bitstamp_mock_test.go @@ -34,6 +34,7 @@ func TestMain(m *testing.M) { bitstampConfig.API.Credentials.Secret = apiSecret bitstampConfig.API.Credentials.ClientID = customerID b.SetDefaults() + b.Websocket = sharedtestvalues.NewTestWebsocket() err = b.Setup(bitstampConfig) if err != nil { log.Fatal("Bitstamp setup error", err) @@ -46,8 +47,6 @@ func TestMain(m *testing.M) { b.HTTPClient = newClient b.API.Endpoints.URL = serverDetails + "/api" - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() log.Printf(sharedtestvalues.MockTesting, b.Name, b.API.Endpoints.URL) os.Exit(m.Run()) } diff --git a/exchanges/bitstamp/bitstamp_test.go b/exchanges/bitstamp/bitstamp_test.go index 7824c752..b8bfd573 100644 --- a/exchanges/bitstamp/bitstamp_test.go +++ b/exchanges/bitstamp/bitstamp_test.go @@ -678,21 +678,27 @@ func TestBitstamp_OHLC(t *testing.T) { } func TestBitstamp_GetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("btcusd") + currencyPair, err := currency.NewPairFromString("btcusd") + if err != nil { + t.Fatal(err) + } start := time.Unix(1546300800, 0) end := time.Unix(1577836799, 0) - _, err := b.GetHistoricCandles(currencyPair, asset.Spot, start, end, kline.OneDay) + _, err = b.GetHistoricCandles(currencyPair, asset.Spot, start, end, kline.OneDay) if err != nil { t.Fatal(err) } } func TestBitstamp_GetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("btcusd") + currencyPair, err := currency.NewPairFromString("btcusd") + if err != nil { + t.Fatal(err) + } start := time.Unix(1546300800, 0) end := time.Unix(1577836799, 0) - _, err := b.GetHistoricCandlesExtended(currencyPair, asset.Spot, start, end, kline.OneDay) + _, err = b.GetHistoricCandlesExtended(currencyPair, asset.Spot, start, end, kline.OneDay) if err != nil { t.Fatal(err) } diff --git a/exchanges/bitstamp/bitstamp_websocket.go b/exchanges/bitstamp/bitstamp_websocket.go index f91d5c88..7d8b72ce 100644 --- a/exchanges/bitstamp/bitstamp_websocket.go +++ b/exchanges/bitstamp/bitstamp_websocket.go @@ -9,11 +9,12 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -24,10 +25,10 @@ const ( // WsConnect connects to a websocket feed func (b *Bitstamp) WsConnect() error { if !b.Websocket.IsEnabled() || !b.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := b.WebsocketConn.Dial(&dialer, http.Header{}) + err := b.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } @@ -38,33 +39,27 @@ func (b *Bitstamp) WsConnect() error { if err != nil { b.Websocket.DataHandler <- err } - b.generateDefaultSubscriptions() + subs, err := b.generateDefaultSubscriptions() + if err != nil { + return err + } go b.wsReadData() - - return nil + return b.Websocket.SubscribeToChannels(subs) } // wsReadData receives and passes on websocket messages for processing func (b *Bitstamp) wsReadData() { b.Websocket.Wg.Add(1) - defer func() { - b.Websocket.Wg.Done() - }() + defer b.Websocket.Wg.Done() + for { - select { - case <-b.Websocket.ShutdownC: + resp := b.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := b.WebsocketConn.ReadMessage() - if err != nil { - b.Websocket.ReadMessageErrors <- err - return - } - b.Websocket.TrafficAlert <- struct{}{} - err = b.wsHandleData(resp.Raw) - if err != nil { - b.Websocket.DataHandler <- err - } + } + err := b.wsHandleData(resp.Raw) + if err != nil { + b.Websocket.DataHandler <- err } } } @@ -97,7 +92,11 @@ func (b *Bitstamp) wsHandleData(respRaw []byte) error { return err } currencyPair := strings.Split(wsResponse.Channel, "_") - p := currency.NewPairFromString(strings.ToUpper(currencyPair[2])) + p, err := currency.NewPairFromString(strings.ToUpper(currencyPair[2])) + if err != nil { + return err + } + err = b.wsUpdateOrderbook(wsOrderBookTemp.Data, p, asset.Spot) if err != nil { return err @@ -109,7 +108,11 @@ func (b *Bitstamp) wsHandleData(respRaw []byte) error { return err } currencyPair := strings.Split(wsResponse.Channel, "_") - p := currency.NewPairFromString(strings.ToUpper(currencyPair[2])) + p, err := currency.NewPairFromString(strings.ToUpper(currencyPair[2])) + if err != nil { + return err + } + side := order.Buy if wsTradeTemp.Data.Type == -1 { side = order.Sell @@ -119,7 +122,7 @@ func (b *Bitstamp) wsHandleData(respRaw []byte) error { if err != nil { return err } - b.Websocket.DataHandler <- wshandler.TradeData{ + b.Websocket.DataHandler <- stream.TradeData{ Timestamp: time.Unix(wsTradeTemp.Data.Timestamp, 0), CurrencyPair: p, AssetType: a, @@ -134,45 +137,73 @@ func (b *Bitstamp) wsHandleData(respRaw []byte) error { log.Debugf(log.ExchangeSys, "%v - Websocket order acknowledgement", b.Name) } default: - b.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: b.Name + wshandler.UnhandledMessage + string(respRaw)} + b.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: b.Name + stream.UnhandledMessage + string(respRaw)} } return nil } -func (b *Bitstamp) generateDefaultSubscriptions() { +func (b *Bitstamp) generateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { var channels = []string{"live_trades_", "order_book_"} - enabledCurrencies := b.GetEnabledPairs(asset.Spot) - var subscriptions []wshandler.WebsocketChannelSubscription + enabledCurrencies, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + var subscriptions []stream.ChannelSubscription for i := range channels { for j := range enabledCurrencies { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channels[i] + enabledCurrencies[j].Lower().String(), + Asset: asset.Spot, }) } } - b.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (b *Bitstamp) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - req := websocketEventRequest{ - Event: "bts:subscribe", - Data: websocketData{ - Channel: channelToSubscribe.Channel, - }, +func (b *Bitstamp) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + req := websocketEventRequest{ + Event: "bts:subscribe", + Data: websocketData{ + Channel: channelsToSubscribe[i].Channel, + }, + } + err := b.Websocket.Conn.SendJSONMessage(req) + if err != nil { + errs = append(errs, err) + continue + } + b.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) } - return b.WebsocketConn.SendJSONMessage(req) + if errs != nil { + return errs + } + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (b *Bitstamp) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - req := websocketEventRequest{ - Event: "bts:unsubscribe", - Data: websocketData{ - Channel: channelToSubscribe.Channel, - }, +func (b *Bitstamp) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToUnsubscribe { + req := websocketEventRequest{ + Event: "bts:unsubscribe", + Data: websocketData{ + Channel: channelsToUnsubscribe[i].Channel, + }, + } + err := b.Websocket.Conn.SendJSONMessage(req) + if err != nil { + errs = append(errs, err) + continue + } + b.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) } - return b.WebsocketConn.SendJSONMessage(req) + if errs != nil { + return errs + } + return nil } func (b *Bitstamp) wsUpdateOrderbook(update websocketOrderBook, p currency.Pair, assetType asset.Item) error { @@ -207,29 +238,22 @@ func (b *Bitstamp) wsUpdateOrderbook(update websocketOrderBook, p currency.Pair, bids = append(bids, orderbook.Item{Price: target, Amount: amount}) } - err := b.Websocket.Orderbook.LoadSnapshot(&orderbook.Base{ + return b.Websocket.Orderbook.LoadSnapshot(&orderbook.Base{ Bids: bids, Asks: asks, Pair: p, LastUpdated: time.Unix(update.Timestamp, 0), - AssetType: asset.Spot, + AssetType: assetType, ExchangeName: b.Name, }) +} + +func (b *Bitstamp) seedOrderBook() error { + p, err := b.GetEnabledPairs(asset.Spot) if err != nil { return err } - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: p, - Asset: assetType, - Exchange: b.Name, - } - - return nil -} - -func (b *Bitstamp) seedOrderBook() error { - p := b.GetEnabledPairs(asset.Spot) for x := range p { orderbookSeed, err := b.GetOrderbook(p[x].String()) if err != nil { @@ -257,12 +281,6 @@ func (b *Bitstamp) seedOrderBook() error { if err != nil { return err } - - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: p[x], - Asset: asset.Spot, - Exchange: b.Name, - } } return nil } diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 79b2d1ca..1ab4624c 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -18,8 +18,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,17 +56,11 @@ func (b *Bitstamp) SetDefaults() { b.API.CredentialsValidator.RequiresSecret = true b.API.CredentialsValidator.RequiresClientID = true - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Uppercase: true} + configFmt := ¤cy.PairFormat{Uppercase: true} + err := b.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } b.Features = exchange.Features{ @@ -137,7 +131,7 @@ func (b *Bitstamp) SetDefaults() { b.API.Endpoints.URLDefault = bitstampAPIURL b.API.Endpoints.URL = b.API.Endpoints.URLDefault b.API.Endpoints.WebsocketURL = bitstampWSURL - b.Websocket = wshandler.New() + b.Websocket = stream.New() b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout b.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -155,34 +149,30 @@ func (b *Bitstamp) Setup(exch *config.ExchangeConfig) error { return err } - err = b.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: bitstampWSURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: b.WsConnect, - Subscriber: b.Subscribe, - UnSubscriber: b.Unsubscribe, - Features: &b.Features.Supports.WebsocketCapabilities, - }) + err = b.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: bitstampWSURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: b.WsConnect, + Subscriber: b.Subscribe, + UnSubscriber: b.Unsubscribe, + GenerateSubscriptions: b.generateDefaultSubscriptions, + Features: &b.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + }) if err != nil { return err } - b.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, + return b.Websocket.SetupNewConnection(stream.ConnectionSetup{ URL: b.Websocket.GetWebsocketURL(), - ProxyURL: b.Websocket.GetProxyAddress(), - Verbose: b.Verbose, ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - return nil + }) } // Start starts the Bitstamp go routine @@ -245,32 +235,35 @@ func (b *Bitstamp) UpdateTradablePairs(forceUpdate bool) error { return err } - return b.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + return b.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (b *Bitstamp) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) tick, err := b.GetTicker(p.String(), false) if err != nil { - return tickerPrice, err + return nil, err } - tickerPrice = &ticker.Price{ - Last: tick.Last, - High: tick.High, - Low: tick.Low, - Bid: tick.Bid, - Ask: tick.Ask, - Volume: tick.Volume, - Open: tick.Open, - Pair: p, - LastUpdated: time.Unix(tick.Timestamp, 0), - } - - err = ticker.ProcessTicker(b.Name, tickerPrice, assetType) + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick.Last, + High: tick.High, + Low: tick.Low, + Bid: tick.Bid, + Ask: tick.Ask, + Volume: tick.Volume, + Open: tick.Open, + Pair: p, + LastUpdated: time.Unix(tick.Timestamp, 0), + ExchangeName: b.Name, + AssetType: assetType}) if err != nil { - return tickerPrice, err + return nil, err } return ticker.GetTicker(b.Name, p, assetType) @@ -548,11 +541,6 @@ func (b *Bitstamp) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdra }, nil } -// GetWebsocket returns a pointer to the exchange websocket -func (b *Bitstamp) GetWebsocket() (*wshandler.Websocket, error) { - return b.Websocket, nil -} - // GetActiveOrders retrieves any orders that are active/open func (b *Bitstamp) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) { var currPair string @@ -580,6 +568,11 @@ func (b *Bitstamp) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, "%s GetActiveOrders unable to parse time: %s\n", b.Name, err) } + pair, err := currency.NewPairFromString(resp[i].Currency) + if err != nil { + return nil, err + } + orders = append(orders, order.Detail{ Amount: resp[i].Amount, ID: strconv.FormatInt(resp[i].ID, 10), @@ -587,7 +580,7 @@ func (b *Bitstamp) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, Type: order.Limit, Side: orderSide, Date: tm, - Pair: currency.NewPairFromString(resp[i].Currency), + Pair: pair, Exchange: b.Name, }) } @@ -604,6 +597,12 @@ func (b *Bitstamp) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, if len(req.Pairs) == 1 { currPair = req.Pairs[0].String() } + + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + resp, err := b.GetUserTransactions(currPair) if err != nil { return nil, err @@ -644,7 +643,7 @@ func (b *Bitstamp) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, if quoteCurrency.String() != "" && baseCurrency.String() != "" { currPair = currency.NewPairWithDelimiter(baseCurrency.String(), quoteCurrency.String(), - b.GetPairFormat(asset.Spot, false).Delimiter) + format.Delimiter) } tm, err := parseTime(resp[i].Date) @@ -666,30 +665,6 @@ func (b *Bitstamp) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *Bitstamp) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - b.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *Bitstamp) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - b.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *Bitstamp) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return b.Websocket.GetSubscriptions(), nil -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (b *Bitstamp) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (b *Bitstamp) ValidateCredentials() error { @@ -712,8 +687,13 @@ func (b *Bitstamp) GetHistoricCandles(pair currency.Pair, a asset.Item, start, e Interval: interval, } + formattedPair, err := b.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + candles, err := b.OHLC( - b.FormatExchangeCurrency(pair, a).Lower().String(), + formattedPair.Lower().String(), start, end, b.FormatExchangeKlineInterval(interval), @@ -759,9 +739,14 @@ func (b *Bitstamp) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, } dates := kline.CalcDateRanges(start, end, interval, b.Features.Enabled.Kline.ResultLimit) + formattedPair, err := b.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + for x := range dates { candles, err := b.OHLC( - b.FormatExchangeCurrency(pair, a).Lower().String(), + formattedPair.Lower().String(), dates[x].Start, dates[x].End, b.FormatExchangeKlineInterval(interval), diff --git a/exchanges/bittrex/bittrex_test.go b/exchanges/bittrex/bittrex_test.go index 0eea30a0..196db8d7 100644 --- a/exchanges/bittrex/bittrex_test.go +++ b/exchanges/bittrex/bittrex_test.go @@ -343,14 +343,19 @@ func TestFormatWithdrawPermissions(t *testing.T) { } func TestGetActiveOrders(t *testing.T) { + p, err := currency.NewPairFromString(currPair) + if err != nil { + t.Fatal(err) + } + var getOrdersRequest = order.GetOrdersRequest{ Type: order.AnyType, - Pairs: []currency.Pair{currency.NewPairFromString(currPair)}, + Pairs: []currency.Pair{p}, } getOrdersRequest.Pairs[0].Delimiter = "-" - _, err := b.GetActiveOrders(&getOrdersRequest) + _, err = b.GetActiveOrders(&getOrdersRequest) if areTestAPIKeysSet() && err != nil { t.Errorf("Could not get open orders: %s", err) } else if !areTestAPIKeysSet() && err == nil { diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index 032aa1f7..1cdf6fe3 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -18,7 +18,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -54,19 +53,11 @@ func (b *Bittrex) SetDefaults() { b.API.CredentialsValidator.RequiresKey = true b.API.CredentialsValidator.RequiresSecret = true - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "-", - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "-", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + configFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + err := b.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } b.Features = exchange.Features{ @@ -113,7 +104,6 @@ func (b *Bittrex) Setup(exch *config.ExchangeConfig) error { b.SetEnabled(false) return nil } - return b.SetupDefaults(exch) } @@ -133,24 +123,53 @@ func (b *Bittrex) Run() { } forceUpdate := false - delim := b.GetPairFormat(asset.Spot, false).Delimiter - if !common.StringDataContains(b.GetEnabledPairs(asset.Spot).Strings(), delim) || - !common.StringDataContains(b.GetAvailablePairs(asset.Spot).Strings(), delim) { + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + b.Name, + err) + return + } + + pairs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + b.Name, + err) + return + } + + avail, err := b.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + b.Name, + err) + return + } + + if !common.StringDataContains(pairs.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { forceUpdate = true log.Warn(log.ExchangeSys, "Available pairs for Bittrex reset due to config upgrade, please enable the ones you would like again") - - err := b.UpdatePairs(currency.NewPairsFromStrings( - []string{currency.USDT.String() + delim + currency.BTC.String()}, - ), - asset.Spot, - true, - true, - ) + pairs, err = currency.NewPairsFromStrings([]string{currency.USDT.String() + + format.Delimiter + + currency.BTC.String()}) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", b.Name, err) + } else { + err = b.UpdatePairs(pairs, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + b.Name, + err) + } } } @@ -158,7 +177,7 @@ func (b *Bittrex) Run() { return } - err := b.UpdateTradablePairs(forceUpdate) + err = b.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", @@ -192,8 +211,12 @@ func (b *Bittrex) UpdateTradablePairs(forceUpdate bool) error { if err != nil { return err } + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } - return b.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + return b.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateAccountInfo Retrieves balances for all enabled currencies for the @@ -239,41 +262,46 @@ func (b *Bittrex) FetchAccountInfo() (account.Holdings, error) { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bittrex) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) ticks, err := b.GetMarketSummaries() if err != nil { - return tickerPrice, err - } - pairs := b.GetEnabledPairs(assetType) - for i := range pairs { - for j := range ticks.Result { - if !strings.EqualFold(ticks.Result[j].MarketName, pairs[i].String()) { - continue - } - tickerTime, err := parseTime(ticks.Result[j].TimeStamp) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s UpdateTicker unable to parse time: %s\n", b.Name, err) - } - tickerPrice = &ticker.Price{ - Last: ticks.Result[j].Last, - High: ticks.Result[j].High, - Low: ticks.Result[j].Low, - Bid: ticks.Result[j].Bid, - Ask: ticks.Result[j].Ask, - Volume: ticks.Result[j].BaseVolume, - QuoteVolume: ticks.Result[j].Volume, - Close: ticks.Result[j].PrevDay, - Pair: pairs[i], - LastUpdated: tickerTime, - } - err = ticker.ProcessTicker(b.Name, tickerPrice, assetType) - if err != nil { - log.Error(log.Ticker, err) - } - } + return nil, err } + pairs, err := b.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + + for j := range ticks.Result { + cp, err := currency.NewPairFromString(ticks.Result[j].MarketName) + if err != nil { + return nil, err + } + if !pairs.Contains(cp, true) { + continue + } + tickerTime, err := parseTime(ticks.Result[j].TimeStamp) + if err != nil { + return nil, err + } + + err = ticker.ProcessTicker(&ticker.Price{ + Last: ticks.Result[j].Last, + High: ticks.Result[j].High, + Low: ticks.Result[j].Low, + Bid: ticks.Result[j].Bid, + Ask: ticks.Result[j].Ask, + Volume: ticks.Result[j].BaseVolume, + QuoteVolume: ticks.Result[j].Volume, + Close: ticks.Result[j].PrevDay, + Pair: cp, + LastUpdated: tickerTime, + ExchangeName: b.Name, + AssetType: assetType}) + if err != nil { + return nil, err + } + } return ticker.GetTicker(b.Name, p, assetType) } @@ -297,12 +325,17 @@ func (b *Bittrex) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderb // UpdateOrderbook updates and returns the orderbook for a currency pair func (b *Bittrex) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - orderbookNew, err := b.GetOrderbook(b.FormatExchangeCurrency(p, assetType).String()) + fpair, err := b.FormatExchangeCurrency(p, assetType) if err != nil { - return orderBook, err + return nil, err } + orderbookNew, err := b.GetOrderbook(fpair.String()) + if err != nil { + return nil, err + } + + orderBook := new(orderbook.Base) for x := range orderbookNew.Result.Buy { orderBook.Bids = append(orderBook.Bids, orderbook.Item{ @@ -453,11 +486,6 @@ func (b *Bittrex) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (b *Bittrex) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrNotYetImplemented -} - // GetFeeByType returns an estimate of fee based on type of transaction func (b *Bittrex) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !b.AllowAuthenticatedRequest() && // Todo check connection status @@ -474,6 +502,11 @@ func (b *Bittrex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, currPair = req.Pairs[0].String() } + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + resp, err := b.GetOpenOrders(currPair) if err != nil { return nil, err @@ -491,8 +524,16 @@ func (b *Bittrex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, resp.Result[i].Opened) } - pair := currency.NewPairDelimiter(resp.Result[i].Exchange, - b.GetPairFormat(asset.Spot, false).Delimiter) + pair, err := currency.NewPairDelimiter(resp.Result[i].Exchange, + format.Delimiter) + if err != nil { + log.Errorf(log.ExchangeSys, + "Exchange %v Func %v Order %v Could not parse currency pair %v", + b.Name, + "GetActiveOrders", + resp.Result[i].OrderUUID, + err) + } orderType := order.Type(strings.ToUpper(resp.Result[i].Type)) orders = append(orders, order.Detail{ @@ -521,6 +562,11 @@ func (b *Bittrex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, currPair = req.Pairs[0].String() } + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + resp, err := b.GetOrderHistoryForCurrency(currPair) if err != nil { return nil, err @@ -533,13 +579,21 @@ func (b *Bittrex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, log.Errorf(log.ExchangeSys, "Exchange %v Func %v Order %v Could not parse date to unix with value of %v", b.Name, - "GetActiveOrders", + "GetOrderHistory", resp.Result[i].OrderUUID, resp.Result[i].Opened) } - pair := currency.NewPairDelimiter(resp.Result[i].Exchange, - b.GetPairFormat(asset.Spot, false).Delimiter) + pair, err := currency.NewPairDelimiter(resp.Result[i].Exchange, + format.Delimiter) + if err != nil { + log.Errorf(log.ExchangeSys, + "Exchange %v Func %v Order %v Could not parse currency pair %v", + b.Name, + "GetOrderHistory", + resp.Result[i].OrderUUID, + err) + } orderType := order.Type(strings.ToUpper(resp.Result[i].Type)) orders = append(orders, order.Detail{ @@ -561,28 +615,6 @@ func (b *Bittrex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *Bittrex) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *Bittrex) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *Bittrex) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (b *Bittrex) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (b *Bittrex) ValidateCredentials() error { diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index 870dc56c..02988dff 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -18,7 +18,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) const ( @@ -72,13 +71,12 @@ const ( heartbeat = "heartbeat" tick = "tick" wsOB = "orderbookUpdate" - tradeEndPoint = "tradeEndPoint" + tradeEndPoint = "trade" ) // BTCMarkets is the overarching type across the BTCMarkets package type BTCMarkets struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } // GetMarkets returns the BTCMarkets instruments @@ -191,7 +189,7 @@ func (b *BTCMarkets) GetMarketCandles(marketID, timeWindow string, from, to time } // GetTickers gets multiple tickers -func (b *BTCMarkets) GetTickers(marketIDs []currency.Pair) ([]Ticker, error) { +func (b *BTCMarkets) GetTickers(marketIDs currency.Pairs) ([]Ticker, error) { var tickers []Ticker params := url.Values{} for x := range marketIDs { @@ -761,7 +759,11 @@ func (b *BTCMarkets) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) { return fee, err } for x := range temp.FeeByMarkets { - if currency.NewPairFromString(temp.FeeByMarkets[x].MarketID) == feeBuilder.Pair { + p, err := currency.NewPairFromString(temp.FeeByMarkets[x].MarketID) + if err != nil { + return 0, err + } + if p == feeBuilder.Pair { fee = temp.FeeByMarkets[x].MakerFeeRate if !feeBuilder.IsMaker { fee = temp.FeeByMarkets[x].TakerFeeRate diff --git a/exchanges/btcmarkets/btcmarkets_test.go b/exchanges/btcmarkets/btcmarkets_test.go index b0df0e52..46acc76d 100644 --- a/exchanges/btcmarkets/btcmarkets_test.go +++ b/exchanges/btcmarkets/btcmarkets_test.go @@ -44,21 +44,17 @@ func TestMain(m *testing.M) { bConfig.API.Credentials.Key = apiKey bConfig.API.Credentials.Secret = apiSecret bConfig.API.AuthenticatedSupport = true - + b.Websocket = sharedtestvalues.NewTestWebsocket() err = b.Setup(bConfig) if err != nil { log.Fatal(err) } - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() - err = b.ValidateCredentials() if err != nil { fmt.Println("API credentials are invalid:", err) b.API.AuthenticatedSupport = false b.API.AuthenticatedWebsocketSupport = false } - os.Exit(m.Run()) } @@ -108,8 +104,11 @@ func TestGetMarketCandles(t *testing.T) { func TestGetTickers(t *testing.T) { t.Parallel() - temp := currency.NewPairsFromStrings([]string{LTCAUD, BTCAUD}) - _, err := b.GetTickers(temp) + temp, err := currency.NewPairsFromStrings([]string{LTCAUD, BTCAUD}) + if err != nil { + t.Fatal(err) + } + _, err = b.GetTickers(temp) if err != nil { t.Error(err) } @@ -720,8 +719,11 @@ func TestWsOrders(t *testing.T) { } func TestBTCMarkets_GetHistoricCandles(t *testing.T) { - p := currency.NewPairFromString(BTCAUD) - _, err := b.GetHistoricCandles(p, asset.Spot, time.Now().Add(-time.Hour*24).UTC(), time.Now().UTC(), kline.OneHour) + p, err := currency.NewPairFromString(BTCAUD) + if err != nil { + t.Fatal(err) + } + _, err = b.GetHistoricCandles(p, asset.Spot, time.Now().Add(-time.Hour*24).UTC(), time.Now().UTC(), kline.OneHour) if err != nil { t.Fatal(err) } @@ -736,8 +738,11 @@ func TestBTCMarkets_GetHistoricCandles(t *testing.T) { func TestBTCMarkets_GetHistoricCandlesExtended(t *testing.T) { start := time.Now().AddDate(0, 0, -1001) end := time.Now() - p := currency.NewPairFromString(BTCAUD) - _, err := b.GetHistoricCandlesExtended(p, asset.Spot, start, end, kline.OneDay) + p, err := currency.NewPairFromString(BTCAUD) + if err != nil { + t.Fatal(err) + } + _, err = b.GetHistoricCandlesExtended(p, asset.Spot, start, end, kline.OneDay) if err != nil { t.Fatal(err) } diff --git a/exchanges/btcmarkets/btcmarkets_types.go b/exchanges/btcmarkets/btcmarkets_types.go index 0592296f..8c9c77a6 100644 --- a/exchanges/btcmarkets/btcmarkets_types.go +++ b/exchanges/btcmarkets/btcmarkets_types.go @@ -337,21 +337,14 @@ type TradingFeeResponse struct { FeeByMarkets []TradingFeeData `json:"FeeByMarkets"` } -// WsSubscribe message sent via ws to subscribe +// WsSubscribe defines a subscription message used in the Subscribe function type WsSubscribe struct { MarketIDs []string `json:"marketIds,omitempty"` - Channels []string `json:"channels"` - MessageType string `json:"messageType"` -} - -// WsAuthSubscribe message sent via login to subscribe -type WsAuthSubscribe struct { - MarketIDs []string `json:"marketIds,omitempty"` - Channels []string `json:"channels"` - Key string `json:"key"` - Signature string `json:"signature"` - Timestamp string `json:"timestamp"` - MessageType string `json:"messageType"` + Channels []string `json:"channels,omitempty"` + Key string `json:"key,omitempty"` + Signature string `json:"signature,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + MessageType string `json:"messageType,omitempty"` } // WsMessageType message sent via ws to determine type @@ -380,6 +373,7 @@ type WsTrade struct { TradeID int64 `json:"tradeId"` Price float64 `json:"price,string"` Volume float64 `json:"volume,string"` + Side string `json:"side"` MessageType string `json:"messageType"` } diff --git a/exchanges/btcmarkets/btcmarkets_websocket.go b/exchanges/btcmarkets/btcmarkets_websocket.go index a5f187a5..7fe37656 100644 --- a/exchanges/btcmarkets/btcmarkets_websocket.go +++ b/exchanges/btcmarkets/btcmarkets_websocket.go @@ -12,13 +12,12 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" - exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -29,10 +28,10 @@ const ( // WsConnect connects to a websocket feed func (b *BTCMarkets) WsConnect() error { if !b.Websocket.IsEnabled() || !b.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := b.WebsocketConn.Dial(&dialer, http.Header{}) + err := b.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } @@ -40,35 +39,26 @@ func (b *BTCMarkets) WsConnect() error { log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", b.Name) } go b.wsReadData() - if b.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - b.createChannels() + subs, err := b.generateDefaultSubscriptions() + if err != nil { + return err } - b.generateDefaultSubscriptions() - return nil + return b.Websocket.SubscribeToChannels(subs) } // wsReadData receives and passes on websocket messages for processing func (b *BTCMarkets) wsReadData() { b.Websocket.Wg.Add(1) - defer func() { - b.Websocket.Wg.Done() - }() + defer b.Websocket.Wg.Done() for { - select { - case <-b.Websocket.ShutdownC: + resp := b.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := b.WebsocketConn.ReadMessage() - if err != nil { - b.Websocket.ReadMessageErrors <- err - return - } - b.Websocket.TrafficAlert <- struct{}{} - err = b.wsHandleData(resp.Raw) - if err != nil { - b.Websocket.DataHandler <- err - } + } + err := b.wsHandleData(resp.Raw) + if err != nil { + b.Websocket.DataHandler <- err } } } @@ -91,7 +81,11 @@ func (b *BTCMarkets) wsHandleData(respRaw []byte) error { return err } - p := currency.NewPairFromString(ob.Currency) + p, err := currency.NewPairFromString(ob.Currency) + if err != nil { + return err + } + var bids, asks []orderbook.Item for x := range ob.Bids { var price, amount float64 @@ -135,7 +129,7 @@ func (b *BTCMarkets) wsHandleData(respRaw []byte) error { ExchangeName: b.Name, }) } else { - err = b.Websocket.Orderbook.Update(&wsorderbook.WebsocketOrderbookUpdate{ + err = b.Websocket.Orderbook.Update(&buffer.Update{ UpdateTime: ob.Timestamp, Asset: asset.Spot, Bids: bids, @@ -147,26 +141,31 @@ func (b *BTCMarkets) wsHandleData(respRaw []byte) error { if err != nil { return err } - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: p, - Asset: asset.Spot, - Exchange: b.Name, - } case tradeEndPoint: var trade WsTrade err := json.Unmarshal(respRaw, &trade) if err != nil { return err } - p := currency.NewPairFromString(trade.Currency) - b.Websocket.DataHandler <- wshandler.TradeData{ + + p, err := currency.NewPairFromString(trade.Currency) + if err != nil { + return err + } + + side := order.Buy + if trade.Side == "Ask" { + side = order.Sell + } + + b.Websocket.DataHandler <- stream.TradeData{ Timestamp: trade.Timestamp, CurrencyPair: p, AssetType: asset.Spot, Exchange: b.Name, Price: trade.Price, Amount: trade.Volume, - Side: order.UnknownSide, + Side: side, EventType: order.UnknownType, } case tick: @@ -176,7 +175,10 @@ func (b *BTCMarkets) wsHandleData(respRaw []byte) error { return err } - p := currency.NewPairFromString(tick.Currency) + p, err := currency.NewPairFromString(tick.Currency) + if err != nil { + return err + } b.Websocket.DataHandler <- &ticker.Price{ ExchangeName: b.Name, @@ -247,12 +249,16 @@ func (b *BTCMarkets) wsHandleData(respRaw []byte) error { Err: err, } } - p := currency.NewPairFromString(orderData.MarketID) - var a asset.Item - a, err = b.GetPairAssetType(p) + + p, err := currency.NewPairFromString(orderData.MarketID) if err != nil { - return err + b.Websocket.DataHandler <- order.ClassificationError{ + Exchange: b.Name, + OrderID: orderID, + Err: err, + } } + b.Websocket.DataHandler <- &order.Detail{ Price: price, Amount: originalAmount, @@ -263,7 +269,7 @@ func (b *BTCMarkets) wsHandleData(respRaw []byte) error { Type: oType, Side: oSide, Status: oStatus, - AssetType: a, + AssetType: asset.Spot, Date: orderData.Timestamp, Trades: trades, Pair: p, @@ -276,91 +282,80 @@ func (b *BTCMarkets) wsHandleData(respRaw []byte) error { } return fmt.Errorf("%v websocket error. Code: %v Message: %v", b.Name, wsErr.Code, wsErr.Message) default: - b.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: b.Name + wshandler.UnhandledMessage + string(respRaw)} + b.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: b.Name + stream.UnhandledMessage + string(respRaw)} return nil } return nil } -func (b *BTCMarkets) generateDefaultSubscriptions() { - var channels = []string{tick, tradeEndPoint, wsOB} - enabledCurrencies := b.GetEnabledPairs(asset.Spot) - var subscriptions []wshandler.WebsocketChannelSubscription +func (b *BTCMarkets) generateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var channels = []string{wsOB, tick, tradeEndPoint} + enabledCurrencies, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + var subscriptions []stream.ChannelSubscription for i := range channels { for j := range enabledCurrencies { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channels[i], Currency: enabledCurrencies[j], + Asset: asset.Spot, }) } } - b.Websocket.SubscribeToChannels(subscriptions) + + var authChannels = []string{fundChange, heartbeat, orderChange} + if b.Websocket.CanUseAuthenticatedEndpoints() { + for i := range authChannels { + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: authChannels[i], + }) + } + } + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (b *BTCMarkets) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - unauthChannels := []string{tick, tradeEndPoint, wsOB} - authChannels := []string{fundChange, heartbeat, orderChange} - switch { - case common.StringDataCompare(unauthChannels, channelToSubscribe.Channel): - req := WsSubscribe{ - MarketIDs: []string{b.FormatExchangeCurrency(channelToSubscribe.Currency, asset.Spot).String()}, - Channels: []string{channelToSubscribe.Channel}, - MessageType: subscribe, - } - err := b.WebsocketConn.SendJSONMessage(req) - if err != nil { - return err - } - case common.StringDataCompare(authChannels, channelToSubscribe.Channel): - message, ok := channelToSubscribe.Params["AuthSub"].(WsAuthSubscribe) - if !ok { - return errors.New("invalid params data") - } - tempAuthData := b.generateAuthSubscriptions() - message.Channels = append(message.Channels, channelToSubscribe.Channel, heartbeat) - message.Key = tempAuthData.Key - message.Signature = tempAuthData.Signature - message.Timestamp = tempAuthData.Timestamp - err := b.WebsocketConn.SendJSONMessage(message) - if err != nil { - return err +func (b *BTCMarkets) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var authChannels = []string{fundChange, heartbeat, orderChange} + + var payload WsSubscribe + payload.MessageType = subscribe + + for i := range channelsToSubscribe { + payload.Channels = append(payload.Channels, + channelsToSubscribe[i].Channel) + + if channelsToSubscribe[i].Currency.String() != "" { + if !common.StringDataCompare(payload.MarketIDs, + channelsToSubscribe[i].Currency.String()) { + payload.MarketIDs = append(payload.MarketIDs, + channelsToSubscribe[i].Currency.String()) + } } } + + for i := range authChannels { + if !common.StringDataCompare(payload.Channels, authChannels[i]) { + continue + } + signTime := strconv.FormatInt(time.Now().UTC().UnixNano()/1000000, 10) + strToSign := "/users/self/subscribe" + "\n" + signTime + tempSign := crypto.GetHMAC(crypto.HashSHA512, + []byte(strToSign), + []byte(b.API.Credentials.Secret)) + sign := crypto.Base64Encode(tempSign) + payload.Key = b.API.Credentials.Key + payload.Signature = sign + payload.Timestamp = signTime + break + } + + err := b.Websocket.Conn.SendJSONMessage(payload) + if err != nil { + return err + } + b.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe...) return nil } - -// Login logs in allowing private ws events -func (b *BTCMarkets) generateAuthSubscriptions() WsAuthSubscribe { - var authSubInfo WsAuthSubscribe - signTime := strconv.FormatInt(time.Now().UTC().UnixNano()/1000000, 10) - strToSign := "/users/self/subscribe" + "\n" + signTime - tempSign := crypto.GetHMAC(crypto.HashSHA512, - []byte(strToSign), - []byte(b.API.Credentials.Secret)) - sign := crypto.Base64Encode(tempSign) - authSubInfo.Key = b.API.Credentials.Key - authSubInfo.Signature = sign - authSubInfo.Timestamp = signTime - return authSubInfo -} - -// createChannels creates channels that need to be -func (b *BTCMarkets) createChannels() { - tempChannels := []string{orderChange, fundChange} - var channels []wshandler.WebsocketChannelSubscription - pairArray := b.GetEnabledPairs(asset.Spot) - for y := range tempChannels { - for x := range pairArray { - var authSub WsAuthSubscribe - var channel wshandler.WebsocketChannelSubscription - channel.Params = make(map[string]interface{}) - channel.Channel = tempChannels[y] - authSub.MarketIDs = append(authSub.MarketIDs, b.FormatExchangeCurrency(pairArray[x], asset.Spot).String()) - authSub.MessageType = subscribe - channel.Params["AuthSub"] = authSub - channels = append(channels, channel) - } - } - b.Websocket.SubscribeToChannels(channels) -} diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 20abdf82..d37372a6 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -59,19 +59,11 @@ func (b *BTCMarkets) SetDefaults() { b.API.Endpoints.URLDefault = btcMarketsAPIURL b.API.Endpoints.URL = b.API.Endpoints.URLDefault - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "-", - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "-", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + configFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + err := b.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } b.Features = exchange.Features{ @@ -131,7 +123,7 @@ func (b *BTCMarkets) SetDefaults() { request.WithLimiter(SetRateLimit())) b.API.Endpoints.WebsocketURL = btcMarketsWSURL - b.Websocket = wshandler.New() + b.Websocket = stream.New() b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout b.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -149,41 +141,30 @@ func (b *BTCMarkets) Setup(exch *config.ExchangeConfig) error { return err } - err = b.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: btcMarketsWSURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: b.WsConnect, - Subscriber: b.Subscribe, - Features: &b.Features.Supports.WebsocketCapabilities, - }) + err = b.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: btcMarketsWSURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: b.WsConnect, + Subscriber: b.Subscribe, + GenerateSubscriptions: b.generateDefaultSubscriptions, + Features: &b.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + BufferEnabled: true, + SortBuffer: true, + }) if err != nil { return err } - b.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: b.Websocket.GetWebsocketURL(), - ProxyURL: b.Websocket.GetProxyAddress(), - Verbose: b.Verbose, + return b.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - b.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - true, - true, - false, - false, - exch.Name) - - return nil + }) } // Start starts the BTC Markets go routine @@ -205,10 +186,34 @@ func (b *BTCMarkets) Run() { btcMarketsWSURL) b.PrintEnabledPairs() } + forceUpdate := false - delim := b.GetPairFormat(asset.Spot, false).Delimiter - if !common.StringDataContains(b.GetEnabledPairs(asset.Spot).Strings(), delim) || - !common.StringDataContains(b.GetAvailablePairs(asset.Spot).Strings(), delim) { + pairs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s Failed to update enabled currencies Err:%s\n", + b.Name, + err) + return + } + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s Failed to update enabled currencies.\n", + b.Name) + return + } + + avail, err := b.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s Failed to update enabled currencies.\n", + b.Name) + return + } + + if !common.StringDataContains(pairs.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { log.Warnln(log.ExchangeSys, "Available pairs for BTC Markets reset due to config upgrade, please enable the pairs you would like again.") forceUpdate = true } @@ -216,10 +221,10 @@ func (b *BTCMarkets) Run() { enabledPairs := currency.Pairs{currency.Pair{ Base: currency.BTC.Lower(), Quote: currency.AUD.Lower(), - Delimiter: delim, + Delimiter: format.Delimiter, }, } - err := b.UpdatePairs(enabledPairs, asset.Spot, true, true) + err = b.UpdatePairs(enabledPairs, asset.Spot, true, true) if err != nil { log.Errorf(log.ExchangeSys, "%s Failed to update enabled currencies.\n", @@ -231,7 +236,7 @@ func (b *BTCMarkets) Run() { return } - err := b.UpdateTradablePairs(forceUpdate) + err = b.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", @@ -264,28 +269,49 @@ func (b *BTCMarkets) UpdateTradablePairs(forceUpdate bool) error { if err != nil { return err } + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } - return b.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + return b.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (b *BTCMarkets) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - allPairs := b.GetEnabledPairs(assetType) - tickers, err := b.GetTickers(allPairs.Slice()) + allPairs, err := b.GetEnabledPairs(assetType) if err != nil { return nil, err } + + tickers, err := b.GetTickers(allPairs) + if err != nil { + return nil, err + } + + if len(allPairs) != len(tickers) { + return nil, errors.New("enabled pairs differ from returned tickers") + } + for x := range tickers { - var resp ticker.Price - resp.Pair = currency.NewPairFromString(tickers[x].MarketID) - resp.Last = tickers[x].LastPrice - resp.High = tickers[x].High24h - resp.Low = tickers[x].Low24h - resp.Bid = tickers[x].BestBID - resp.Ask = tickers[x].BestAsk - resp.Volume = tickers[x].Volume - resp.LastUpdated = time.Now() - err = ticker.ProcessTicker(b.Name, &resp, assetType) + var newP currency.Pair + newP, err = currency.NewPairFromString(tickers[x].MarketID) + if err != nil { + return nil, err + } + + err = ticker.ProcessTicker(&ticker.Price{ + Pair: newP, + Last: tickers[x].LastPrice, + High: tickers[x].High24h, + Low: tickers[x].Low24h, + Bid: tickers[x].BestBID, + Ask: tickers[x].BestAsk, + Volume: tickers[x].Volume, + LastUpdated: time.Now(), + ExchangeName: b.Name, + AssetType: assetType, + }) if err != nil { return nil, err } @@ -313,11 +339,17 @@ func (b *BTCMarkets) FetchOrderbook(p currency.Pair, assetType asset.Item) (*ord // UpdateOrderbook updates and returns the orderbook for a currency pair func (b *BTCMarkets) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - tempResp, err := b.GetOrderbook(b.FormatExchangeCurrency(p, assetType).String(), 2) + fpair, err := b.FormatExchangeCurrency(p, assetType) if err != nil { - return orderBook, err + return nil, err } + + tempResp, err := b.GetOrderbook(fpair.String(), 2) + if err != nil { + return nil, err + } + + orderBook := new(orderbook.Base) for x := range tempResp.Bids { orderBook.Bids = append(orderBook.Bids, orderbook.Item{ Amount: tempResp.Bids[x].Volume, @@ -401,7 +433,12 @@ func (b *BTCMarkets) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) s.Side = order.Bid } - tempResp, err := b.NewOrder(b.FormatExchangeCurrency(s.Pair, asset.Spot).String(), + fpair, err := b.FormatExchangeCurrency(s.Pair, asset.Spot) + if err != nil { + return resp, err + } + + tempResp, err := b.NewOrder(fpair.String(), s.Price, s.Amount, s.Type.String(), @@ -468,9 +505,15 @@ func (b *BTCMarkets) GetOrderInfo(orderID string) (order.Detail, error) { if err != nil { return resp, err } + + p, err := currency.NewPairFromString(o.MarketID) + if err != nil { + return order.Detail{}, err + } + resp.Exchange = b.Name resp.ID = orderID - resp.Pair = currency.NewPairFromString(o.MarketID) + resp.Pair = p resp.Price = o.Price resp.Date = o.CreationTime resp.ExecutedAmount = o.Amount - o.OpenAmount @@ -569,11 +612,6 @@ func (b *BTCMarkets) WithdrawFiatFundsToInternationalBank(withdrawRequest *withd return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (b *BTCMarkets) GetWebsocket() (*wshandler.Websocket, error) { - return b.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (b *BTCMarkets) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !b.AllowAuthenticatedRequest() && // Todo check connection status @@ -586,7 +624,10 @@ func (b *BTCMarkets) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, err // GetActiveOrders retrieves any orders that are active/open func (b *BTCMarkets) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) { if len(req.Pairs) == 0 { - allPairs := b.GetEnabledPairs(asset.Spot) + allPairs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } for a := range allPairs { req.Pairs = append(req.Pairs, allPairs[a]) @@ -595,7 +636,11 @@ func (b *BTCMarkets) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detai var resp []order.Detail for x := range req.Pairs { - tempData, err := b.GetOrders(b.FormatExchangeCurrency(req.Pairs[x], asset.Spot).String(), -1, -1, -1, true) + fpair, err := b.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } + tempData, err := b.GetOrders(fpair.String(), -1, -1, -1, true) if err != nil { return resp, err } @@ -666,7 +711,12 @@ func (b *BTCMarkets) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detai } } for y := range req.Pairs { - orders, err := b.GetOrders(b.FormatExchangeCurrency(req.Pairs[y], asset.Spot).String(), -1, -1, -1, false) + fpair, err := b.FormatExchangeCurrency(req.Pairs[y], asset.Spot) + if err != nil { + return nil, err + } + + orders, err := b.GetOrders(fpair.String(), -1, -1, -1, false) if err != nil { return resp, err } @@ -697,8 +747,14 @@ func (b *BTCMarkets) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detai case orderAccepted: continue } + + p, err := currency.NewPairFromString(tempData.Orders[c].MarketID) + if err != nil { + return nil, err + } + tempResp.Exchange = b.Name - tempResp.Pair = currency.NewPairFromString(tempData.Orders[c].MarketID) + tempResp.Pair = p tempResp.Side = order.Bid if tempData.Orders[c].Side == ask { tempResp.Side = order.Ask @@ -713,28 +769,6 @@ func (b *BTCMarkets) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detai return resp, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *BTCMarkets) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *BTCMarkets) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *BTCMarkets) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (b *BTCMarkets) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (b *BTCMarkets) ValidateCredentials() error { @@ -776,7 +810,12 @@ func (b *BTCMarkets) GetHistoricCandles(pair currency.Pair, a asset.Item, start, return kline.Item{}, errors.New(kline.ErrRequestExceedsExchangeLimits) } - candles, err := b.GetMarketCandles(b.FormatExchangeCurrency(pair, a).String(), + formattedPair, err := b.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + + candles, err := b.GetMarketCandles(formattedPair.String(), b.FormatExchangeKlineInterval(interval), start, end, @@ -789,7 +828,7 @@ func (b *BTCMarkets) GetHistoricCandles(pair currency.Pair, a asset.Item, start, } ret := kline.Item{ Exchange: b.Name, - Pair: b.FormatExchangeCurrency(pair, a), + Pair: formattedPair, Asset: asset.Spot, Interval: interval, } diff --git a/exchanges/btse/btse.go b/exchanges/btse/btse.go index 24914e4c..5c93d016 100644 --- a/exchanges/btse/btse.go +++ b/exchanges/btse/btse.go @@ -15,19 +15,18 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) // BTSE is the overarching type across this package type BTSE struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } const ( - btseAPIURL = "https://api.btse.com" - btseAPIPath = "/spot/v2/" + btseAPIURL = "https://api.btse.com" + btseSPOTAPIPath = "/spot/v2/" + btseFuturesAPIPath = "/futures/api/v2.1/" // Public endpoints btseMarketOverview = "market_summary" @@ -50,34 +49,40 @@ const ( // GetMarketsSummary stores market summary data func (b *BTSE) GetMarketsSummary() (*HighLevelMarketData, error) { var m HighLevelMarketData - return &m, b.SendHTTPRequest(http.MethodGet, btseMarketOverview, &m) + return &m, b.SendHTTPRequest(http.MethodGet, btseMarketOverview, &m, true) } -// GetMarkets returns a list of markets available on BTSE -func (b *BTSE) GetMarkets() ([]Market, error) { - var m []Market - return m, b.SendHTTPRequest(http.MethodGet, btseMarkets, &m) +// GetSpotMarkets returns a list of spot markets available on BTSE +func (b *BTSE) GetSpotMarkets() ([]SpotMarket, error) { + var m []SpotMarket + return m, b.SendHTTPRequest(http.MethodGet, btseMarkets, &m, true) +} + +// GetFuturesMarkets returns a list of futures markets available on BTSE +func (b *BTSE) GetFuturesMarkets() ([]FuturesMarket, error) { + var m []FuturesMarket + return m, b.SendHTTPRequest(http.MethodGet, btseMarketOverview, &m, false) } // FetchOrderBook gets orderbook data for a given pair func (b *BTSE) FetchOrderBook(symbol string) (*Orderbook, error) { var o Orderbook endpoint := fmt.Sprintf("%s/%s", btseOrderbook, symbol) - return &o, b.SendHTTPRequest(http.MethodGet, endpoint, &o) + return &o, b.SendHTTPRequest(http.MethodGet, endpoint, &o, true) } // GetTrades returns a list of trades for the specified symbol func (b *BTSE) GetTrades(symbol string) ([]Trade, error) { var t []Trade endpoint := fmt.Sprintf("%s/%s", btseTrades, symbol) - return t, b.SendHTTPRequest(http.MethodGet, endpoint, &t) + return t, b.SendHTTPRequest(http.MethodGet, endpoint, &t, true) } // GetTicker returns the ticker for a specified symbol func (b *BTSE) GetTicker(symbol string) (*Ticker, error) { var t Ticker endpoint := fmt.Sprintf("%s/%s", btseTicker, symbol) - err := b.SendHTTPRequest(http.MethodGet, endpoint, &t) + err := b.SendHTTPRequest(http.MethodGet, endpoint, &t, true) if err != nil { return nil, err } @@ -88,19 +93,19 @@ func (b *BTSE) GetTicker(symbol string) (*Ticker, error) { func (b *BTSE) GetMarketStatistics(symbol string) (*MarketStatistics, error) { var m MarketStatistics endpoint := fmt.Sprintf("%s/%s", btseStats, symbol) - return &m, b.SendHTTPRequest(http.MethodGet, endpoint, &m) + return &m, b.SendHTTPRequest(http.MethodGet, endpoint, &m, true) } // GetServerTime returns the exchanges server time func (b *BTSE) GetServerTime() (*ServerTime, error) { var s ServerTime - return &s, b.SendHTTPRequest(http.MethodGet, btseTime, &s) + return &s, b.SendHTTPRequest(http.MethodGet, btseTime, &s, true) } // GetAccountBalance returns the users account balance func (b *BTSE) GetAccountBalance() ([]CurrencyBalance, error) { var a []CurrencyBalance - return a, b.SendAuthenticatedHTTPRequest(http.MethodGet, btseAccount, nil, &a) + return a, b.SendAuthenticatedHTTPRequest(http.MethodGet, btseAccount, nil, &a, true) } // CreateOrder creates an order @@ -129,7 +134,7 @@ func (b *BTSE) CreateOrder(amount, price float64, side, orderType, symbol, timeI } var r orderResp - return &r.ID, b.SendAuthenticatedHTTPRequest(http.MethodPost, btseOrder, req, &r) + return &r.ID, b.SendAuthenticatedHTTPRequest(http.MethodPost, btseOrder, req, &r, true) } // GetOrders returns all pending orders @@ -139,7 +144,7 @@ func (b *BTSE) GetOrders(symbol string) ([]OpenOrder, error) { req["symbol"] = symbol } var o []OpenOrder - return o, b.SendAuthenticatedHTTPRequest(http.MethodGet, btsePendingOrders, req, &o) + return o, b.SendAuthenticatedHTTPRequest(http.MethodGet, btsePendingOrders, req, &o, true) } // CancelExistingOrder cancels an order @@ -148,7 +153,7 @@ func (b *BTSE) CancelExistingOrder(orderID, symbol string) (*CancelOrder, error) req := make(map[string]interface{}) req["order_id"] = orderID req["symbol"] = symbol - return &c, b.SendAuthenticatedHTTPRequest(http.MethodPost, btseDeleteOrder, req, &c) + return &c, b.SendAuthenticatedHTTPRequest(http.MethodPost, btseDeleteOrder, req, &c, true) } // GetFills gets all filled orders @@ -184,14 +189,18 @@ func (b *BTSE) GetFills(orderID, symbol, before, after, limit, username string) } var o []FilledOrder - return o, b.SendAuthenticatedHTTPRequest(http.MethodPost, btseFills, req, &o) + return o, b.SendAuthenticatedHTTPRequest(http.MethodPost, btseFills, req, &o, true) } // SendHTTPRequest sends an HTTP request to the desired endpoint -func (b *BTSE) SendHTTPRequest(method, endpoint string, result interface{}) error { +func (b *BTSE) SendHTTPRequest(method, endpoint string, result interface{}, spotEndpoint bool) error { + p := btseSPOTAPIPath + if !spotEndpoint { + p = btseFuturesAPIPath + } return b.SendPayload(context.Background(), &request.Item{ Method: method, - Path: b.API.Endpoints.URL + btseAPIPath + endpoint, + Path: b.API.Endpoints.URL + p + endpoint, Result: result, Verbose: b.Verbose, HTTPDebugging: b.HTTPDebugging, @@ -200,12 +209,18 @@ func (b *BTSE) SendHTTPRequest(method, endpoint string, result interface{}) erro } // SendAuthenticatedHTTPRequest sends an authenticated HTTP request to the desired endpoint -func (b *BTSE) SendAuthenticatedHTTPRequest(method, endpoint string, req map[string]interface{}, result interface{}) error { +func (b *BTSE) SendAuthenticatedHTTPRequest(method, endpoint string, req map[string]interface{}, result interface{}, spotEndpoint bool) error { if !b.AllowAuthenticatedRequest() { return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, b.Name) } - path := btseAPIPath + endpoint + + p := btseSPOTAPIPath + if !spotEndpoint { + p = btseFuturesAPIPath + } + + path := p + endpoint headers := make(map[string]string) headers["btse-api"] = b.API.Credentials.Key nonce := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) diff --git a/exchanges/btse/btse_test.go b/exchanges/btse/btse_test.go index ab190711..6582fd38 100644 --- a/exchanges/btse/btse_test.go +++ b/exchanges/btse/btse_test.go @@ -39,13 +39,11 @@ func TestMain(m *testing.M) { btseConfig.API.AuthenticatedSupport = true btseConfig.API.Credentials.Key = apiKey btseConfig.API.Credentials.Secret = apiSecret - + b.Websocket = sharedtestvalues.NewTestWebsocket() err = b.Setup(btseConfig) if err != nil { log.Fatal(err) } - b.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - b.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -61,9 +59,17 @@ func TestGetMarketsSummary(t *testing.T) { } } -func TestGetMarkets(t *testing.T) { +func TestGetSpotMarkets(t *testing.T) { t.Parallel() - _, err := b.GetMarkets() + _, err := b.GetSpotMarkets() + if err != nil { + t.Error(err) + } +} + +func TestGetFuturesMarkets(t *testing.T) { + t.Parallel() + _, err := b.GetFuturesMarkets() if err != nil { t.Error(err) } @@ -411,3 +417,16 @@ func TestStatusToStandardStatus(t *testing.T) { } } } + +func TestFetchTradablePairs(t *testing.T) { + assets := b.GetAssetTypes() + for i := range assets { + data, err := b.FetchTradablePairs(assets[i]) + if err != nil { + t.Fatal(err) + } + if len(data) == 0 { + t.Fatal("data cannot be zero") + } + } +} diff --git a/exchanges/btse/btse_types.go b/exchanges/btse/btse_types.go index e6d9b935..133fcc4b 100644 --- a/exchanges/btse/btse_types.go +++ b/exchanges/btse/btse_types.go @@ -21,8 +21,8 @@ type OverviewData struct { // HighLevelMarketData stores market overview data type HighLevelMarketData map[string]OverviewData -// Market stores market data -type Market struct { +// SpotMarket stores market data +type SpotMarket struct { Symbol string `json:"symbol"` ID string `json:"id"` BaseCurrency string `json:"base_currency"` @@ -35,6 +35,41 @@ type Market struct { Status string `json:"status"` } +// FuturesMarket stores market data +type FuturesMarket struct { + Symbol string `json:"symbol"` + Last float64 `json:"last"` + LowestAsk float64 `json:"lowestAsk"` + HighestBid float64 `json:"highestBid"` + OpenInterest float64 `json:"openInterest"` + OpenInterestUSD float64 `json:"openInterestUSD"` + PercentageChange float64 `json:"percentageChange"` + Volume float64 `json:"volume"` + High24Hr float64 `json:"high24Hr"` + Low24Hr float64 `json:"low24Hr"` + Base string `json:"base"` + Quote string `json:"quote"` + ContractStart int64 `json:"contractStart"` + ContractEnd int64 `json:"contractEnd"` + Active bool `json:"active"` + TimeBasedContract bool `json:"timeBasedContract"` + OpenTime int64 `json:"openTime"` + CloseTime int64 `json:"closeTime"` + StartMatching int64 `json:"startMatching"` + InactiveTime int64 `json:"inactiveTime"` + FundingRate float64 `json:"fundingRate"` + ContractSize float64 `json:"contractSize"` + MaxPosition int64 `json:"maxPosition"` + MinValidPrice float64 `json:"minValidPrice"` + MinPriceIncrement float64 `json:"minPriceIncrement"` + MinOrderSize int32 `json:"minOrderSize"` + MaxOrderSize int32 `json:"maxOrderSize"` + MinRiskLimit int32 `json:"minRiskLimit"` + MaxRiskLimit int32 `json:"maxRiskLimit"` + MinSizeIncrement float64 `json:"minSizeIncrement"` + AvailableSettlement []string `json:"availableSettlement"` +} + // Trade stores trade data type Trade struct { SerialID string `json:"serial_id"` diff --git a/exchanges/btse/btse_websocket.go b/exchanges/btse/btse_websocket.go index 5402dd79..743b70e8 100644 --- a/exchanges/btse/btse_websocket.go +++ b/exchanges/btse/btse_websocket.go @@ -16,25 +16,25 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" ) const ( btseWebsocket = "wss://ws.btse.com/spotWS" - btseWebsocketTimer = 57 * time.Second + btseWebsocketTimer = time.Second * 57 ) // WsConnect connects the websocket client func (b *BTSE) WsConnect() error { if !b.Websocket.IsEnabled() || !b.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := b.WebsocketConn.Dial(&dialer, http.Header{}) + err := b.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } - b.WebsocketConn.SetupPingHandler(wshandler.WebsocketPingHandler{ + b.Websocket.Conn.SetupPingHandler(stream.PingHandler{ MessageType: websocket.PingMessage, Delay: btseWebsocketTimer, }) @@ -48,17 +48,19 @@ func (b *BTSE) WsConnect() error { } } - b.GenerateDefaultSubscriptions() - return nil + subs, err := b.GenerateDefaultSubscriptions() + if err != nil { + return err + } + return b.Websocket.SubscribeToChannels(subs) } // WsAuthenticate Send an authentication message to receive auth data func (b *BTSE) WsAuthenticate() error { nonce := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) path := "/spotWS" + nonce - hmac := crypto.GetHMAC( - crypto.HashSHA512_384, - []byte((path + nonce)), + hmac := crypto.GetHMAC(crypto.HashSHA512_384, + []byte((path)), []byte(b.API.Credentials.Secret), ) sign := crypto.HexEncodeToString(hmac) @@ -66,7 +68,7 @@ func (b *BTSE) WsAuthenticate() error { Operation: "authKeyExpires", Arguments: []string{b.API.Credentials.Key, nonce, sign}, } - return b.WebsocketConn.SendJSONMessage(req) + return b.Websocket.Conn.SendJSONMessage(req) } func stringToOrderStatus(status string) (order.Status, error) { @@ -93,27 +95,16 @@ func stringToOrderStatus(status string) (order.Status, error) { // wsReadData receives and passes on websocket messages for processing func (b *BTSE) wsReadData() { b.Websocket.Wg.Add(1) - - defer func() { - b.Websocket.Wg.Done() - }() + defer b.Websocket.Wg.Done() for { - select { - case <-b.Websocket.ShutdownC: + resp := b.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - - default: - resp, err := b.WebsocketConn.ReadMessage() - if err != nil { - b.Websocket.ReadMessageErrors <- err - return - } - b.Websocket.TrafficAlert <- struct{}{} - err = b.wsHandleData(resp.Raw) - if err != nil { - b.Websocket.DataHandler <- err - } + } + err := b.wsHandleData(resp.Raw) + if err != nil { + b.Websocket.DataHandler <- err } } } @@ -123,6 +114,13 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { var result Result err := json.Unmarshal(respRaw, &result) if err != nil { + if strings.Contains(string(respRaw), "UNLOGIN_USER connect success") || + strings.Contains(string(respRaw), "authenticated successfully") { + return nil + } else if strings.Contains(string(respRaw), "AUTHENTICATE ERROR") { + b.Websocket.SetCanUseAuthenticatedEndpoints(false) + return errors.New("authentication failure") + } return err } switch { @@ -160,12 +158,19 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { Err: err, } } - p := currency.NewPairFromString(notification.Data[i].Symbol) + + var p currency.Pair + p, err = currency.NewPairFromString(notification.Data[i].Symbol) + if err != nil { + return err + } + var a asset.Item a, err = b.GetPairAssetType(p) if err != nil { return err } + b.Websocket.DataHandler <- &order.Detail{ Price: notification.Data[i].Price, Amount: notification.Data[i].Size, @@ -192,13 +197,21 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { if tradeHistory.Data[x].Gain == -1 { side = order.Sell } - p := currency.NewPairFromString(strings.Replace(tradeHistory.Topic, "tradeHistory:", "", 1)) + + var p currency.Pair + p, err = currency.NewPairFromString(strings.Replace(tradeHistory.Topic, + "tradeHistory:", + "", + 1)) + if err != nil { + return err + } var a asset.Item a, err = b.GetPairAssetType(p) if err != nil { return err } - b.Websocket.DataHandler <- wshandler.TradeData{ + b.Websocket.DataHandler <- stream.TradeData{ Timestamp: time.Unix(0, tradeHistory.Data[x].TransactionTime*int64(time.Millisecond)), CurrencyPair: p, AssetType: a, @@ -248,7 +261,10 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { Amount: amount, }) } - p := currency.NewPairFromString(t.Topic[strings.Index(t.Topic, ":")+1 : strings.Index(t.Topic, "_")]) + p, err := currency.NewPairFromString(t.Topic[strings.Index(t.Topic, ":")+1 : strings.Index(t.Topic, "_")]) + if err != nil { + return err + } var a asset.Item a, err = b.GetPairAssetType(p) if err != nil { @@ -261,50 +277,65 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { if err != nil { return err } - b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{Pair: newOB.Pair, - Asset: a, - Exchange: b.Name} default: - b.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: b.Name + wshandler.UnhandledMessage + string(respRaw)} + b.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: b.Name + stream.UnhandledMessage + string(respRaw)} return nil } return nil } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (b *BTSE) GenerateDefaultSubscriptions() { +func (b *BTSE) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { var channels = []string{"orderBookApi:%s_0", "tradeHistory:%s"} - pairs := b.GetEnabledPairs(asset.Spot) - var subscriptions []wshandler.WebsocketChannelSubscription + pairs, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + var subscriptions []stream.ChannelSubscription if b.Websocket.CanUseAuthenticatedEndpoints() { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: "notificationApi", }) } for i := range channels { for j := range pairs { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: fmt.Sprintf(channels[i], pairs[j]), Currency: pairs[j], + Asset: asset.Spot, }) } } - b.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (b *BTSE) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { +func (b *BTSE) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { var sub wsSub sub.Operation = "subscribe" - sub.Arguments = []string{channelToSubscribe.Channel} - - return b.WebsocketConn.SendJSONMessage(sub) + for i := range channelsToSubscribe { + sub.Arguments = append(sub.Arguments, channelsToSubscribe[i].Channel) + } + err := b.Websocket.Conn.SendJSONMessage(sub) + if err != nil { + return err + } + b.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe...) + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (b *BTSE) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { +func (b *BTSE) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { var unSub wsSub unSub.Operation = "unsubscribe" - unSub.Arguments = []string{channelToSubscribe.Channel} - return b.WebsocketConn.SendJSONMessage(unSub) + for i := range channelsToUnsubscribe { + unSub.Arguments = append(unSub.Arguments, + channelsToUnsubscribe[i].Channel) + } + err := b.Websocket.Conn.SendJSONMessage(unSub) + if err != nil { + return err + } + b.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe...) + return nil } diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index 9efd9ca3..a9ed0c92 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,11 +56,7 @@ func (b *BTSE) SetDefaults() { b.API.CredentialsValidator.RequiresKey = true b.API.CredentialsValidator.RequiresSecret = true - b.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, + fmt1 := currency.PairStore{ RequestFormat: ¤cy.PairFormat{ Uppercase: true, Delimiter: "-", @@ -70,6 +66,23 @@ func (b *BTSE) SetDefaults() { Delimiter: "-", }, } + err := b.StoreAssetPairFormat(asset.Spot, fmt1) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + fmt2 := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{ + Uppercase: true, + }, + ConfigFormat: ¤cy.PairFormat{ + Uppercase: true, + }, + } + err = b.StoreAssetPairFormat(asset.Futures, fmt2) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } b.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ @@ -112,7 +125,7 @@ func (b *BTSE) SetDefaults() { b.API.Endpoints.URLDefault = btseAPIURL b.API.Endpoints.URL = b.API.Endpoints.URLDefault - b.Websocket = wshandler.New() + b.Websocket = stream.New() b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout b.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -130,41 +143,29 @@ func (b *BTSE) Setup(exch *config.ExchangeConfig) error { return err } - err = b.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: btseWebsocket, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: b.WsConnect, - Subscriber: b.Subscribe, - UnSubscriber: b.Unsubscribe, - Features: &b.Features.Supports.WebsocketCapabilities, - }) + err = b.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: btseWebsocket, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: b.WsConnect, + Subscriber: b.Subscribe, + UnSubscriber: b.Unsubscribe, + GenerateSubscriptions: b.GenerateDefaultSubscriptions, + Features: &b.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + }) if err != nil { return err } - b.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: b.Name, - URL: b.Websocket.GetWebsocketURL(), - ProxyURL: b.Websocket.GetProxyAddress(), - Verbose: b.Verbose, + return b.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - b.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - false, - false, - false, - exch.Name) - return nil + }) } // Start starts the BTSE go routine @@ -194,61 +195,96 @@ func (b *BTSE) Run() { } // FetchTradablePairs returns a list of the exchanges tradable pairs -func (b *BTSE) FetchTradablePairs(asset asset.Item) ([]string, error) { - m, err := b.GetMarkets() - if err != nil { - return nil, err +func (b *BTSE) FetchTradablePairs(a asset.Item) ([]string, error) { + var currencies []string + if a == asset.Spot { + m, err := b.GetSpotMarkets() + if err != nil { + return nil, err + } + + for x := range m { + if m[x].Status != "active" { + continue + } + currencies = append(currencies, m[x].Symbol) + } + } else if a == asset.Futures { + m, err := b.GetFuturesMarkets() + if err != nil { + return nil, err + } + + for x := range m { + if !m[x].Active { + continue + } + currencies = append(currencies, m[x].Symbol) + } } - var currencies []string - for x := range m { - if m[x].Status != "active" { - continue - } - currencies = append(currencies, m[x].Symbol) - } return currencies, nil } // UpdateTradablePairs updates the exchanges available pairs and stores // them in the exchanges config func (b *BTSE) UpdateTradablePairs(forceUpdate bool) error { - pairs, err := b.FetchTradablePairs(asset.Spot) - if err != nil { - return err - } + a := b.GetAssetTypes() + for i := range a { + pairs, err := b.FetchTradablePairs(a[i]) + if err != nil { + return err + } - return b.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + err = b.UpdatePairs(p, a[i], false, forceUpdate) + if err != nil { + return err + } + } + return nil } // UpdateTicker updates and returns the ticker for a currency pair func (b *BTSE) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) - - t, err := b.GetTicker(b.FormatExchangeCurrency(p, - assetType).String()) - if err != nil { - return tickerPrice, err + if assetType == asset.Futures { + // Futures REST implementation needs to be done before this can be + // removed + return nil, common.ErrNotYetImplemented } - s, err := b.GetMarketStatistics(b.FormatExchangeCurrency(p, - assetType).String()) + fpair, err := b.FormatExchangeCurrency(p, assetType) if err != nil { - return tickerPrice, err + return nil, err } - tickerPrice.Pair = p - tickerPrice.Ask = t.Ask - tickerPrice.Bid = t.Bid - tickerPrice.Low = s.Low - tickerPrice.Last = t.Price - tickerPrice.Volume = s.Volume - tickerPrice.High = s.High - tickerPrice.LastUpdated = s.Time - - err = ticker.ProcessTicker(b.Name, tickerPrice, assetType) + t, err := b.GetTicker(fpair.String()) if err != nil { - return tickerPrice, err + return nil, err + } + + s, err := b.GetMarketStatistics(fpair.String()) + if err != nil { + return nil, err + } + + err = ticker.ProcessTicker(&ticker.Price{ + Pair: p, + Ask: t.Ask, + Bid: t.Bid, + Low: s.Low, + Last: t.Price, + Volume: s.Volume, + High: s.High, + LastUpdated: s.Time, + ExchangeName: b.Name, + AssetType: assetType}) + if err != nil { + return nil, err } return ticker.GetTicker(b.Name, p, assetType) } @@ -273,11 +309,22 @@ func (b *BTSE) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbook // UpdateOrderbook updates and returns the orderbook for a currency pair func (b *BTSE) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - a, err := b.FetchOrderBook(b.FormatExchangeCurrency(p, assetType).String()) - if err != nil { - return orderBook, err + if assetType == asset.Futures { + // Futures REST implementation needs to be done before this can be + // removed + return nil, common.ErrNotYetImplemented } + + fpair, err := b.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + a, err := b.FetchOrderBook(fpair.String()) + if err != nil { + return nil, err + } + + orderBook := new(orderbook.Base) for x := range a.BuyQuote { orderBook.Bids = append(orderBook.Bids, orderbook.Item{ Price: a.BuyQuote[x].Price, @@ -360,11 +407,16 @@ func (b *BTSE) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { return resp, err } + fpair, err := b.FormatExchangeCurrency(s.Pair, s.AssetType) + if err != nil { + return resp, err + } + r, err := b.CreateOrder(s.Amount, s.Price, s.Side.String(), s.Type.String(), - b.FormatExchangeCurrency(s.Pair, asset.Spot).String(), + fpair.String(), goodTillCancel, s.ClientID) if err != nil { @@ -389,9 +441,13 @@ func (b *BTSE) ModifyOrder(action *order.Modify) (string, error) { // CancelOrder cancels an order by its corresponding ID number func (b *BTSE) CancelOrder(order *order.Cancel) error { - r, err := b.CancelExistingOrder(order.ID, - b.FormatExchangeCurrency(order.Pair, - asset.Spot).String()) + fpair, err := b.FormatExchangeCurrency(order.Pair, + order.AssetType) + if err != nil { + return err + } + + r, err := b.CancelExistingOrder(order.ID, fpair.String()) if err != nil { return err } @@ -411,19 +467,28 @@ func (b *BTSE) CancelOrder(order *order.Cancel) error { // If not specified, all orders of all markets will be cancelled func (b *BTSE) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) { var resp order.CancelAllResponse - markets, err := b.GetMarkets() + markets, err := b.GetSpotMarkets() + if err != nil { + return resp, err + } + + format, err := b.GetPairFormat(orderCancellation.AssetType, false) if err != nil { return resp, err } resp.Status = make(map[string]string) for x := range markets { - strPair := b.FormatExchangeCurrency(orderCancellation.Pair, - orderCancellation.AssetType).String() + fair, err := b.FormatExchangeCurrency(orderCancellation.Pair, + orderCancellation.AssetType) + if err != nil { + return resp, err + } + checkPair := currency.NewPairWithDelimiter(markets[x].BaseCurrency, markets[x].QuoteCurrency, - b.GetPairFormat(asset.Spot, false).Delimiter).String() - if strPair != "" && strPair != checkPair { + format.Delimiter).String() + if fair.String() != checkPair { continue } else { orders, err := b.GetOrders(checkPair) @@ -455,6 +520,11 @@ func (b *BTSE) GetOrderInfo(orderID string) (order.Detail, error) { return od, errors.New("no orders found") } + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + return order.Detail{}, err + } + for i := range o { if o[i].ID != orderID { continue @@ -465,8 +535,14 @@ func (b *BTSE) GetOrderInfo(orderID string) (order.Detail, error) { side = order.Sell } - od.Pair = currency.NewPairDelimiter(o[i].Symbol, - b.GetPairFormat(asset.Spot, false).Delimiter) + od.Pair, err = currency.NewPairDelimiter(o[i].Symbol, + format.Delimiter) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s GetOrderInfo unable to parse currency pair: %s\n", + b.Name, + err) + } od.Exchange = b.Name od.Amount = o[i].Amount od.ID = o[i].ID @@ -530,11 +606,6 @@ func (b *BTSE) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.Re return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (b *BTSE) GetWebsocket() (*wshandler.Websocket, error) { - return b.Websocket, nil -} - // GetActiveOrders retrieves any orders that are active/open func (b *BTSE) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) { resp, err := b.GetOrders("") @@ -542,6 +613,11 @@ func (b *BTSE) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, err return nil, err } + format, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range resp { var side = order.Buy @@ -557,9 +633,17 @@ func (b *BTSE) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, err err) } + p, err := currency.NewPairDelimiter(resp[i].Symbol, + format.Delimiter) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s GetActiveOrders unable to parse currency pair: %s\n", + b.Name, + err) + } + openOrder := order.Detail{ - Pair: currency.NewPairDelimiter(resp[i].Symbol, - b.GetPairFormat(asset.Spot, false).Delimiter), + Pair: p, Exchange: b.Name, Amount: resp[i].Amount, ID: resp[i].ID, @@ -621,30 +705,6 @@ func (b *BTSE) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { return b.GetFee(feeBuilder) } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (b *BTSE) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - b.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (b *BTSE) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - b.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (b *BTSE) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return b.Websocket.GetSubscriptions(), nil -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (b *BTSE) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (b *BTSE) ValidateCredentials() error { diff --git a/exchanges/coinbasepro/coinbasepro.go b/exchanges/coinbasepro/coinbasepro.go index 63d08cdf..11a6effb 100644 --- a/exchanges/coinbasepro/coinbasepro.go +++ b/exchanges/coinbasepro/coinbasepro.go @@ -18,7 +18,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -57,7 +56,6 @@ const ( // CoinbasePro is the overarching type across the coinbasepro package type CoinbasePro struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } // GetProducts returns supported currency pairs on the exchange with specific diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index 0237027f..d3dd077b 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -17,7 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/banking" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -49,12 +49,11 @@ func TestMain(m *testing.M) { gdxConfig.API.Credentials.ClientID = clientID gdxConfig.API.AuthenticatedSupport = true gdxConfig.API.AuthenticatedWebsocketSupport = true + c.Websocket = sharedtestvalues.NewTestWebsocket() err = c.Setup(gdxConfig) if err != nil { log.Fatal("CoinbasePro setup error", err) } - c.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - c.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -593,26 +592,24 @@ func TestGetDepositAddress(t *testing.T) { // TestWsAuth dials websocket, sends login request. func TestWsAuth(t *testing.T) { if !c.Websocket.IsEnabled() && !c.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) - } - c.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: c.Name, - URL: c.Websocket.GetWebsocketURL(), - Verbose: c.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + t.Skip(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := c.WebsocketConn.Dial(&dialer, http.Header{}) + err := c.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } - c.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - c.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() go c.wsReadData() - err = c.Subscribe(wshandler.WebsocketChannelSubscription{ - Channel: "user", - Currency: currency.NewPairFromString(testPair), + + p, err := currency.NewPairFromString(testPair) + if err != nil { + t.Fatal(err) + } + err = c.Subscribe([]stream.ChannelSubscription{ + { + Channel: "user", + Currency: p, + }, }) if err != nil { t.Error(err) @@ -947,3 +944,64 @@ func TestParseTime(t *testing.T) { t.Error("unexpected result") } } + +func TestCheckInterval(t *testing.T) { + interval := time.Minute + i, err := checkInterval(interval) + if err != nil { + t.Fatal(err) + } + if i != 60 { + t.Fatal("incorrect return") + } + interval = time.Minute * 5 + i, err = checkInterval(interval) + if err != nil { + t.Fatal(err) + } + if i != 300 { + t.Fatal("incorrect return") + } + + interval = time.Minute * 15 + i, err = checkInterval(interval) + if err != nil { + t.Fatal(err) + } + if i != 900 { + t.Fatal("incorrect return") + } + + interval = time.Hour + i, err = checkInterval(interval) + if err != nil { + t.Fatal(err) + } + if i != 3600 { + t.Fatal("incorrect return") + } + + interval = time.Hour * 6 + i, err = checkInterval(interval) + if err != nil { + t.Fatal(err) + } + if i != 21600 { + t.Fatal("incorrect return") + } + + interval = time.Hour * 24 + i, err = checkInterval(interval) + if err != nil { + t.Fatal(err) + } + if i != 86400 { + t.Fatal("incorrect return") + } + + interval = time.Hour * 1337 + _, err = checkInterval(interval) + if err == nil { + t.Fatal("error cannot be nil") + } +} diff --git a/exchanges/coinbasepro/coinbasepro_types.go b/exchanges/coinbasepro/coinbasepro_types.go index b8cb052f..e02f0556 100644 --- a/exchanges/coinbasepro/coinbasepro_types.go +++ b/exchanges/coinbasepro/coinbasepro_types.go @@ -353,7 +353,7 @@ type FillResponse struct { // WebsocketSubscribe takes in subscription information type WebsocketSubscribe struct { Type string `json:"type"` - ProductID string `json:"product_id,omitempty"` + ProductIDs []string `json:"product_ids,omitempty"` Channels []WsChannels `json:"channels,omitempty"` Signature string `json:"signature,omitempty"` Key string `json:"key,omitempty"` @@ -364,7 +364,7 @@ type WebsocketSubscribe struct { // WsChannels defines outgoing channels for subscription purposes type WsChannels struct { Name string `json:"name"` - ProductIDs []string `json:"product_ids"` + ProductIDs []string `json:"product_ids,omitempty"` } // wsOrderReceived holds websocket received values diff --git a/exchanges/coinbasepro/coinbasepro_websocket.go b/exchanges/coinbasepro/coinbasepro_websocket.go index c785cf4a..6dad3340 100644 --- a/exchanges/coinbasepro/coinbasepro_websocket.go +++ b/exchanges/coinbasepro/coinbasepro_websocket.go @@ -9,6 +9,7 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" @@ -16,9 +17,9 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" ) const ( @@ -28,18 +29,20 @@ const ( // WsConnect initiates a websocket connection func (c *CoinbasePro) WsConnect() error { if !c.Websocket.IsEnabled() || !c.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := c.WebsocketConn.Dial(&dialer, http.Header{}) + err := c.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } - c.GenerateDefaultSubscriptions() + subs, err := c.GenerateDefaultSubscriptions() + if err != nil { + return err + } go c.wsReadData() - - return nil + return c.Websocket.SubscribeToChannels(subs) } // wsReadData receives and passes on websocket messages for processing @@ -51,20 +54,13 @@ func (c *CoinbasePro) wsReadData() { }() for { - select { - case <-c.Websocket.ShutdownC: + resp := c.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := c.WebsocketConn.ReadMessage() - if err != nil { - c.Websocket.ReadMessageErrors <- err - return - } - c.Websocket.TrafficAlert <- struct{}{} - err = c.wsHandleData(resp.Raw) - if err != nil { - c.Websocket.DataHandler <- err - } + } + err := c.wsHandleData(resp.Raw) + if err != nil { + c.Websocket.DataHandler <- err } } } @@ -233,7 +229,7 @@ func (c *CoinbasePro) wsHandleData(respRaw []byte) error { }, } default: - c.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: c.Name + wshandler.UnhandledMessage + string(respRaw)} + c.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: c.Name + stream.UnhandledMessage + string(respRaw)} return nil } return nil @@ -289,23 +285,16 @@ func (c *CoinbasePro) ProcessSnapshot(snapshot *WebsocketOrderbookSnapshot) erro orderbook.Item{Price: price, Amount: amount}) } - pair := currency.NewPairFromString(snapshot.ProductID) - base.AssetType = asset.Spot - base.Pair = pair - base.ExchangeName = c.Name - - err := c.Websocket.Orderbook.LoadSnapshot(&base) + pair, err := currency.NewPairFromString(snapshot.ProductID) if err != nil { return err } - c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: pair, - Asset: asset.Spot, - Exchange: c.Name, - } + base.AssetType = asset.Spot + base.Pair = pair + base.ExchangeName = c.Name - return nil + return c.Websocket.Orderbook.LoadSnapshot(&base) } // ProcessUpdate updates the orderbook local cache @@ -313,8 +302,14 @@ func (c *CoinbasePro) ProcessUpdate(update WebsocketL2Update) error { var asks, bids []orderbook.Item for i := range update.Changes { - price, _ := strconv.ParseFloat(update.Changes[i][1].(string), 64) - volume, _ := strconv.ParseFloat(update.Changes[i][2].(string), 64) + price, err := strconv.ParseFloat(update.Changes[i][1].(string), 64) + if err != nil { + return err + } + volume, err := strconv.ParseFloat(update.Changes[i][2].(string), 64) + if err != nil { + return err + } if update.Changes[i][0].(string) == order.Buy.Lower() { bids = append(bids, orderbook.Item{Price: price, Amount: volume}) @@ -327,91 +322,123 @@ func (c *CoinbasePro) ProcessUpdate(update WebsocketL2Update) error { return errors.New("coinbasepro_websocket.go error - no data in websocket update") } - p := currency.NewPairFromString(update.ProductID) + p, err := currency.NewPairFromString(update.ProductID) + if err != nil { + return err + } + timestamp, err := time.Parse(time.RFC3339, update.Time) if err != nil { return err } - err = c.Websocket.Orderbook.Update(&wsorderbook.WebsocketOrderbookUpdate{ + return c.Websocket.Orderbook.Update(&buffer.Update{ Bids: bids, Asks: asks, Pair: p, UpdateTime: timestamp, Asset: asset.Spot, }) - if err != nil { - return err - } - - c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: p, - Asset: asset.Spot, - Exchange: c.Name, - } - - return nil } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (c *CoinbasePro) GenerateDefaultSubscriptions() { +func (c *CoinbasePro) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { var channels = []string{"heartbeat", "level2", "ticker", "user"} - enabledCurrencies := c.GetEnabledPairs(asset.Spot) - var subscriptions []wshandler.WebsocketChannelSubscription + enabledCurrencies, err := c.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + var subscriptions []stream.ChannelSubscription for i := range channels { - if (channels[i] == "user" || channels[i] == "full") && !c.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + if (channels[i] == "user" || channels[i] == "full") && + !c.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { continue } for j := range enabledCurrencies { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: channels[i], - Currency: c.FormatExchangeCurrency(enabledCurrencies[j], - asset.Spot), + fpair, err := c.FormatExchangeCurrency(enabledCurrencies[j], + asset.Spot) + if err != nil { + return nil, err + } + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: channels[i], + Currency: fpair, + Asset: asset.Spot, }) } } - c.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (c *CoinbasePro) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { +func (c *CoinbasePro) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { subscribe := WebsocketSubscribe{ Type: "subscribe", - Channels: []WsChannels{ - { - Name: channelToSubscribe.Channel, - ProductIDs: []string{ - c.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String(), - }, - }, - }, } - if channelToSubscribe.Channel == "user" || channelToSubscribe.Channel == "full" { - n := strconv.FormatInt(time.Now().Unix(), 10) - message := n + http.MethodGet + "/users/self/verify" - hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(message), - []byte(c.API.Credentials.Secret)) - subscribe.Signature = crypto.Base64Encode(hmac) - subscribe.Key = c.API.Credentials.Key - subscribe.Passphrase = c.API.Credentials.ClientID - subscribe.Timestamp = n + +subscriptions: + for i := range channelsToSubscribe { + p := channelsToSubscribe[i].Currency.String() + if !common.StringDataCompare(subscribe.ProductIDs, p) && p != "" { + subscribe.ProductIDs = append(subscribe.ProductIDs, p) + } + + for j := range subscribe.Channels { + if subscribe.Channels[j].Name == channelsToSubscribe[i].Channel { + continue subscriptions + } + } + + subscribe.Channels = append(subscribe.Channels, WsChannels{ + Name: channelsToSubscribe[i].Channel, + }) + + if channelsToSubscribe[i].Channel == "user" || + channelsToSubscribe[i].Channel == "full" { + n := strconv.FormatInt(time.Now().Unix(), 10) + message := n + http.MethodGet + "/users/self/verify" + hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(message), + []byte(c.API.Credentials.Secret)) + subscribe.Signature = crypto.Base64Encode(hmac) + subscribe.Key = c.API.Credentials.Key + subscribe.Passphrase = c.API.Credentials.ClientID + subscribe.Timestamp = n + } } - return c.WebsocketConn.SendJSONMessage(subscribe) + err := c.Websocket.Conn.SendJSONMessage(subscribe) + if err != nil { + return err + } + c.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe...) + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (c *CoinbasePro) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - subscribe := WebsocketSubscribe{ +func (c *CoinbasePro) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + unsubscribe := WebsocketSubscribe{ Type: "unsubscribe", - Channels: []WsChannels{ - { - Name: channelToSubscribe.Channel, - ProductIDs: []string{ - c.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String(), - }, - }, - }, } - return c.WebsocketConn.SendJSONMessage(subscribe) + +unsubscriptions: + for i := range channelsToUnsubscribe { + p := channelsToUnsubscribe[i].Currency.String() + if !common.StringDataCompare(unsubscribe.ProductIDs, p) && p != "" { + unsubscribe.ProductIDs = append(unsubscribe.ProductIDs, p) + } + + for j := range unsubscribe.Channels { + if unsubscribe.Channels[j].Name == channelsToUnsubscribe[i].Channel { + continue unsubscriptions + } + } + + unsubscribe.Channels = append(unsubscribe.Channels, WsChannels{ + Name: channelsToUnsubscribe[i].Channel, + }) + } + err := c.Websocket.Conn.SendJSONMessage(unsubscribe) + if err != nil { + return err + } + c.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe...) + return nil } diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 868cd5ea..b707fb98 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -58,19 +58,11 @@ func (c *CoinbasePro) SetDefaults() { c.API.CredentialsValidator.RequiresClientID = true c.API.CredentialsValidator.RequiresBase64DecodeSecret = true - c.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "-", - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "-", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + configFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + err := c.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } c.Features = exchange.Features{ @@ -141,7 +133,7 @@ func (c *CoinbasePro) SetDefaults() { c.API.Endpoints.URLDefault = coinbaseproAPIURL c.API.Endpoints.URL = c.API.Endpoints.URLDefault c.API.Endpoints.WebsocketURL = coinbaseproWebsocketURL - c.Websocket = wshandler.New() + c.Websocket = stream.New() c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout c.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -159,41 +151,31 @@ func (c *CoinbasePro) Setup(exch *config.ExchangeConfig) error { return err } - err = c.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: coinbaseproWebsocketURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: c.WsConnect, - Subscriber: c.Subscribe, - UnSubscriber: c.Unsubscribe, - Features: &c.Features.Supports.WebsocketCapabilities, - }) + err = c.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: coinbaseproWebsocketURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: c.WsConnect, + Subscriber: c.Subscribe, + UnSubscriber: c.Unsubscribe, + GenerateSubscriptions: c.GenerateDefaultSubscriptions, + Features: &c.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + BufferEnabled: true, + SortBuffer: true, + }) if err != nil { return err } - c.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: c.Name, - URL: c.Websocket.GetWebsocketURL(), - ProxyURL: c.Websocket.GetProxyAddress(), - Verbose: c.Verbose, + return c.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - c.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - true, - true, - false, - false, - exch.Name) - return nil + }) } // Start starts the coinbasepro go routine @@ -217,21 +199,55 @@ func (c *CoinbasePro) Run() { } forceUpdate := false - delim := c.GetPairFormat(asset.Spot, false).Delimiter - if !common.StringDataContains(c.CurrencyPairs.GetPairs(asset.Spot, - true).Strings(), delim) || - !common.StringDataContains(c.CurrencyPairs.GetPairs(asset.Spot, - false).Strings(), delim) { - enabledPairs := currency.NewPairsFromStrings( - []string{currency.BTC.String() + delim + currency.USD.String()}, - ) - log.Warn(log.ExchangeSys, - "Enabled pairs for CoinbasePro reset due to config upgrade, please enable the ones you would like to use again") - forceUpdate = true + format, err := c.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } + enabled, err := c.CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } - err := c.UpdatePairs(enabledPairs, asset.Spot, true, true) + avail, err := c.CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + + format.Delimiter + + currency.USD.String()}) if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", c.Name, err) + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + } else { + log.Warn(log.ExchangeSys, + "Enabled pairs for CoinbasePro reset due to config upgrade, please enable the ones you would like to use again") + forceUpdate = true + + err = c.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + } } } @@ -239,7 +255,7 @@ func (c *CoinbasePro) Run() { return } - err := c.UpdateTradablePairs(forceUpdate) + err = c.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", c.Name, err) } @@ -252,10 +268,15 @@ func (c *CoinbasePro) FetchTradablePairs(asset asset.Item) ([]string, error) { return nil, err } + format, err := c.GetPairFormat(asset, false) + if err != nil { + return nil, err + } + var products []string for x := range pairs { products = append(products, pairs[x].BaseCurrency+ - c.GetPairFormat(asset, false).Delimiter+ + format.Delimiter+ pairs[x].QuoteCurrency) } @@ -270,7 +291,12 @@ func (c *CoinbasePro) UpdateTradablePairs(forceUpdate bool) error { return err } - return c.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + return c.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateAccountInfo retrieves balances for all enabled currencies for the @@ -317,28 +343,34 @@ func (c *CoinbasePro) FetchAccountInfo() (account.Holdings, error) { // UpdateTicker updates and returns the ticker for a currency pair func (c *CoinbasePro) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tick, err := c.GetTicker(c.FormatExchangeCurrency(p, assetType).String()) + fpair, err := c.FormatExchangeCurrency(p, assetType) if err != nil { return nil, err } - stats, err := c.GetStats(c.FormatExchangeCurrency(p, assetType).String()) + + tick, err := c.GetTicker(fpair.String()) + if err != nil { + return nil, err + } + stats, err := c.GetStats(fpair.String()) if err != nil { return nil, err } tickerPrice := &ticker.Price{ - Last: stats.Last, - High: stats.High, - Low: stats.Low, - Bid: tick.Bid, - Ask: tick.Ask, - Volume: tick.Volume, - Open: stats.Open, - Pair: p, - LastUpdated: tick.Time, - } + Last: stats.Last, + High: stats.High, + Low: stats.Low, + Bid: tick.Bid, + Ask: tick.Ask, + Volume: tick.Volume, + Open: stats.Open, + Pair: p, + LastUpdated: tick.Time, + ExchangeName: c.Name, + AssetType: assetType} - err = ticker.ProcessTicker(c.Name, tickerPrice, assetType) + err = ticker.ProcessTicker(tickerPrice) if err != nil { return tickerPrice, err } @@ -366,15 +398,18 @@ func (c *CoinbasePro) FetchOrderbook(p currency.Pair, assetType asset.Item) (*or // UpdateOrderbook updates and returns the orderbook for a currency pair func (c *CoinbasePro) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - orderbookNew, err := c.GetOrderbook(c.FormatExchangeCurrency(p, - assetType).String(), 2) + fpair, err := c.FormatExchangeCurrency(p, assetType) if err != nil { - return orderBook, err + return nil, err + } + + orderbookNew, err := c.GetOrderbook(fpair.String(), 2) + if err != nil { + return nil, err } obNew := orderbookNew.(OrderbookL1L2) - + orderBook := new(orderbook.Base) for x := range obNew.Bids { orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: obNew.Bids[x].Amount, Price: obNew.Bids[x].Price}) } @@ -413,15 +448,19 @@ func (c *CoinbasePro) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) return submitOrderResponse, err } + fpair, err := c.FormatExchangeCurrency(s.Pair, asset.Spot) + if err != nil { + return submitOrderResponse, err + } + var response string - var err error switch s.Type { case order.Market: response, err = c.PlaceMarketOrder("", s.Amount, s.Amount, s.Side.Lower(), - c.FormatExchangeCurrency(s.Pair, asset.Spot).String(), + fpair.String(), "") case order.Limit: response, err = c.PlaceLimitOrder("", @@ -430,7 +469,7 @@ func (c *CoinbasePro) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) s.Side.Lower(), "", "", - c.FormatExchangeCurrency(s.Pair, asset.Spot).String(), + fpair.String(), "", false) default: @@ -491,10 +530,15 @@ func (c *CoinbasePro) GetOrderInfo(orderID string) (order.Detail, error) { if errOss != nil { return order.Detail{}, fmt.Errorf("error parsing order side: %s", errOss) } + p, errP := currency.NewPairDelimiter(genOrderDetail.ProductID, "-") + if errP != nil { + return order.Detail{}, fmt.Errorf("error parsing order side: %s", errP) + } + response := order.Detail{ Exchange: c.GetName(), ID: genOrderDetail.ID, - Pair: currency.NewPairDelimiter(genOrderDetail.ProductID, "-"), + Pair: p, Side: ss, Type: tt, Date: od, @@ -587,11 +631,6 @@ func (c *CoinbasePro) WithdrawFiatFundsToInternationalBank(withdrawRequest *with }, nil } -// GetWebsocket returns a pointer to the exchange websocket -func (c *CoinbasePro) GetWebsocket() (*wshandler.Websocket, error) { - return c.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (c *CoinbasePro) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !c.AllowAuthenticatedRequest() && // Todo check connection status @@ -605,18 +644,32 @@ func (c *CoinbasePro) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, er func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) { var respOrders []GeneralizedOrderResponse for i := range req.Pairs { + fpair, err := c.FormatExchangeCurrency(req.Pairs[i], asset.Spot) + if err != nil { + return nil, err + } + resp, err := c.GetOrders([]string{"open", "pending", "active"}, - c.FormatExchangeCurrency(req.Pairs[i], asset.Spot).String()) + fpair.String()) if err != nil { return nil, err } respOrders = append(respOrders, resp...) } + format, err := c.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range respOrders { - curr := currency.NewPairDelimiter(respOrders[i].ProductID, - c.GetPairFormat(asset.Spot, false).Delimiter) + var curr currency.Pair + curr, err = currency.NewPairDelimiter(respOrders[i].ProductID, + format.Delimiter) + if err != nil { + return nil, err + } orderSide := order.Side(strings.ToUpper(respOrders[i].Side)) orderType := order.Type(strings.ToUpper(respOrders[i].Type)) orders = append(orders, order.Detail{ @@ -642,18 +695,31 @@ func (c *CoinbasePro) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Deta func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) { var respOrders []GeneralizedOrderResponse for i := range req.Pairs { + fpair, err := c.FormatExchangeCurrency(req.Pairs[i], asset.Spot) + if err != nil { + return nil, err + } resp, err := c.GetOrders([]string{"done", "settled"}, - c.FormatExchangeCurrency(req.Pairs[i], asset.Spot).String()) + fpair.String()) if err != nil { return nil, err } respOrders = append(respOrders, resp...) } + format, err := c.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range respOrders { - curr := currency.NewPairDelimiter(respOrders[i].ProductID, - c.GetPairFormat(asset.Spot, false).Delimiter) + var curr currency.Pair + curr, err = currency.NewPairDelimiter(respOrders[i].ProductID, + format.Delimiter) + if err != nil { + return nil, err + } orderSide := order.Side(strings.ToUpper(respOrders[i].Side)) orderType := order.Type(strings.ToUpper(respOrders[i].Type)) orders = append(orders, order.Detail{ @@ -674,28 +740,23 @@ func (c *CoinbasePro) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Deta return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (c *CoinbasePro) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - c.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (c *CoinbasePro) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - c.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (c *CoinbasePro) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return c.Websocket.GetSubscriptions(), nil -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (c *CoinbasePro) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported +// checkInterval checks allowable interval +func checkInterval(i time.Duration) (int64, error) { + switch i.Seconds() { + case 60: + return 60, nil + case 300: + return 300, nil + case 900: + return 900, nil + case 3600: + return 3600, nil + case 21600: + return 21600, nil + case 86400: + return 86400, nil + } + return 0, fmt.Errorf("interval not allowed %v", i.Seconds()) } // GetHistoricCandles returns a set of candle between two time periods for a @@ -722,7 +783,13 @@ func (c *CoinbasePro) GetHistoricCandles(p currency.Pair, a asset.Item, start, e if err != nil { return kline.Item{}, err } - history, err := c.GetHistoricRates(c.FormatExchangeCurrency(p, a).String(), + + formatP, err := c.FormatExchangeCurrency(p, a) + if err != nil { + return kline.Item{}, err + } + + history, err := c.GetHistoricRates(formatP.String(), start.Format(time.RFC3339), end.Format(time.RFC3339), gran) @@ -765,8 +832,14 @@ func (c *CoinbasePro) GetHistoricCandlesExtended(p currency.Pair, a asset.Item, return kline.Item{}, err } dates := kline.CalcDateRanges(start, end, interval, c.Features.Enabled.Kline.ResultLimit) + + formattedPair, err := c.FormatExchangeCurrency(p, a) + if err != nil { + return kline.Item{}, err + } + for x := range dates { - history, err := c.GetHistoricRates(c.FormatExchangeCurrency(p, a).String(), + history, err := c.GetHistoricRates(formattedPair.String(), dates[x].Start.Format(time.RFC3339), dates[x].End.Format(time.RFC3339), gran) diff --git a/exchanges/coinbene/coinbene.go b/exchanges/coinbene/coinbene.go index ffc4d028..9080d838 100644 --- a/exchanges/coinbene/coinbene.go +++ b/exchanges/coinbene/coinbene.go @@ -18,13 +18,11 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) // Coinbene is the overarching type across this package type Coinbene struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } const ( @@ -35,10 +33,11 @@ const ( coinbeneAPIVersion = "v2" // Public endpoints - coinbeneGetTicker = "/market/ticker/one" - coinbeneGetTickers = "/market/tickers" - coinbeneGetOrderBook = "/market/orderBook" - coinbeneGetKlines = "/market/klines" + coinbeneGetTicker = "/market/ticker/one" + coinbeneGetTickersSpot = "/market/ticker/list" + coinbeneGetTickers = "/market/tickers" + coinbeneGetOrderBook = "/market/orderBook" + coinbeneGetKlines = "/market/klines" // TODO: Implement function --- coinbeneSpotKlines = "/market/instruments/candles" coinbeneSpotExchangeRate = "/market/rate/list" @@ -65,12 +64,15 @@ const ( coinbeneListSwapPositions = "/position/list" coinbenePositionFeeRate = "/position/feeRate" - limitOrder = "1" - marketOrder = "2" - buyDirection = "1" - openLong = "openLong" - openShort = "openShort" - sellDirection = "2" + limitOrder = "1" + marketOrder = "2" + postOnlyOrder = "8" + fillOrKillOrder = "9" + iosOrder = "10" + buyDirection = "1" + openLong = "openLong" + openShort = "openShort" + sellDirection = "2" ) // GetAllPairs gets all pairs on the exchange @@ -162,7 +164,7 @@ func (c *Coinbene) GetTickers() ([]TickerData, error) { TickerData []TickerData `json:"data"` }{} - path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneGetTicker + path := c.API.Endpoints.URL + coinbeneAPIVersion + coinbeneGetTickersSpot return resp.TickerData, c.SendHTTPRequest(path, spotTickerList, &resp) } @@ -266,9 +268,15 @@ func (c *Coinbene) PlaceSpotOrder(price, quantity float64, symbol, direction, params.Set("orderType", limitOrder) case order.Market.Lower(): params.Set("orderType", marketOrder) + case order.PostOnly.Lower(): + params.Set("orderType", postOnlyOrder) + case order.FillOrKill.Lower(): + params.Set("orderType", fillOrKillOrder) + case order.IOS.Lower(): + params.Set("orderType", iosOrder) default: return resp, - errors.New("invalid order type, must be either 'limit' or 'market'") + errors.New("invalid order type, must be either 'limit', 'market', 'postOnly', 'fillOrKill', 'ios'") } params.Set("symbol", symbol) diff --git a/exchanges/coinbene/coinbene_test.go b/exchanges/coinbene/coinbene_test.go index d2ffb463..01baac97 100644 --- a/exchanges/coinbene/coinbene_test.go +++ b/exchanges/coinbene/coinbene_test.go @@ -40,13 +40,11 @@ func TestMain(m *testing.M) { coinbeneConfig.API.AuthenticatedSupport = true coinbeneConfig.API.Credentials.Secret = testAPISecret coinbeneConfig.API.Credentials.Key = testAPIKey - + c.Websocket = sharedtestvalues.NewTestWebsocket() err = c.Setup(coinbeneConfig) if err != nil { log.Fatal(err) } - c.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - c.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -288,7 +286,11 @@ func TestGetSwapOrderbook(t *testing.T) { func TestGetKlines(t *testing.T) { t.Parallel() - _, err := c.GetKlines(currency.NewPairFromString(spotTestPair).String(), + p, err := currency.NewPairFromString(spotTestPair) + if err != nil { + t.Fatal(err) + } + _, err = c.GetKlines(p.String(), time.Now().Add(-time.Hour*1), time.Now(), "1") if err != nil { t.Fatal(err) @@ -297,7 +299,11 @@ func TestGetKlines(t *testing.T) { func TestGetSwapKlines(t *testing.T) { t.Parallel() - _, err := c.GetSwapKlines(currency.NewPairFromString(swapTestPair).String(), + p, err := currency.NewPairFromString(swapTestPair) + if err != nil { + t.Fatal(err) + } + _, err = c.GetSwapKlines(p.String(), time.Now().Add(-time.Hour*1), time.Now(), "1") if err != nil { t.Error(err) @@ -480,8 +486,8 @@ func TestWsUnsubscribe(t *testing.T) { func TestWsLogin(t *testing.T) { pressXToJSON := []byte(`{"event":"login","success":true}`) err := c.wsHandleData(pressXToJSON) - if err != nil { - t.Error(err) + if err == nil { + t.Error("error cannot be nil as this will initiate an auth subscription") } pressXToJSON = []byte(`{"event":"login","success":false}`) @@ -692,14 +698,20 @@ func TestWsUserOrder(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString(spotTestPair) + currencyPair, err := currency.NewPairFromString(spotTestPair) + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 24) - _, err := c.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) + _, err = c.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) if err != nil { t.Fatal(err) } - currencyPairSwap := currency.NewPairFromString(swapTestPair) + currencyPairSwap, err := currency.NewPairFromString(swapTestPair) + if err != nil { + t.Fatal(err) + } _, err = c.GetHistoricCandles(currencyPairSwap, asset.PerpetualSwap, startTime, time.Now(), kline.OneHour) if err != nil { t.Fatal(err) @@ -707,9 +719,12 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString(spotTestPair) + currencyPair, err := currency.NewPairFromString(spotTestPair) + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 24) - _, err := c.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) + _, err = c.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) if err != nil { t.Fatal(err) } diff --git a/exchanges/coinbene/coinbene_websocket.go b/exchanges/coinbene/coinbene_websocket.go index 26e9359c..bc364ce2 100644 --- a/exchanges/coinbene/coinbene_websocket.go +++ b/exchanges/coinbene/coinbene_websocket.go @@ -16,9 +16,9 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" ) const ( @@ -27,15 +27,13 @@ const ( topic = "topic" ) -var comms = make(chan wshandler.WebsocketResponse) - // WsConnect connects to websocket func (c *Coinbene) WsConnect() error { if !c.Websocket.IsEnabled() || !c.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := c.WebsocketConn.Dial(&dialer, http.Header{}) + err := c.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } @@ -48,38 +46,44 @@ func (c *Coinbene) WsConnect() error { c.Websocket.SetCanUseAuthenticatedEndpoints(false) } } - c.GenerateDefaultSubscriptions() - - return nil + subs, err := c.GenerateDefaultSubscriptions() + if err != nil { + return err + } + return c.Websocket.SubscribeToChannels(subs) } // GenerateDefaultSubscriptions generates stuff -func (c *Coinbene) GenerateDefaultSubscriptions() { +func (c *Coinbene) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { var channels = []string{"orderBook.%s.100", "tradeList.%s", "ticker.%s", "kline.%s"} - var subscriptions []wshandler.WebsocketChannelSubscription - pairs := c.GetEnabledPairs(asset.PerpetualSwap) + var subscriptions []stream.ChannelSubscription + pairs, err := c.GetEnabledPairs(asset.PerpetualSwap) + if err != nil { + return nil, err + } for x := range channels { for y := range pairs { pairs[y].Delimiter = "" - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: fmt.Sprintf(channels[x], pairs[y]), Currency: pairs[y], + Asset: asset.PerpetualSwap, }) } } - c.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // GenerateAuthSubs generates auth subs -func (c *Coinbene) GenerateAuthSubs() { - var subscriptions []wshandler.WebsocketChannelSubscription - var sub wshandler.WebsocketChannelSubscription +func (c *Coinbene) GenerateAuthSubs() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription + var sub stream.ChannelSubscription var userChannels = []string{"user.account", "user.position", "user.order"} for z := range userChannels { sub.Channel = userChannels[z] subscriptions = append(subscriptions, sub) } - c.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // wsReadData receives and passes on websocket messages for processing @@ -87,27 +91,20 @@ func (c *Coinbene) wsReadData() { c.Websocket.Wg.Add(1) defer c.Websocket.Wg.Done() for { - select { - case <-c.Websocket.ShutdownC: + resp := c.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := c.WebsocketConn.ReadMessage() - if err != nil { - c.Websocket.ReadMessageErrors <- err - return - } - err = c.wsHandleData(resp.Raw) - if err != nil { - c.Websocket.DataHandler <- err - } + } + err := c.wsHandleData(resp.Raw) + if err != nil { + c.Websocket.DataHandler <- err } } } func (c *Coinbene) wsHandleData(respRaw []byte) error { - c.Websocket.TrafficAlert <- struct{}{} - if string(respRaw) == wshandler.Ping { - err := c.WebsocketConn.SendRawMessage(websocket.TextMessage, []byte(wshandler.Pong)) + if string(respRaw) == stream.Ping { + err := c.Websocket.Conn.SendRawMessage(websocket.TextMessage, []byte(stream.Pong)) if err != nil { return err } @@ -128,8 +125,12 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { if ok && strings.Contains(result[event].(string), "login") { if result["success"].(bool) { c.Websocket.SetCanUseAuthenticatedEndpoints(true) - c.GenerateAuthSubs() - return nil + var authsubs []stream.ChannelSubscription + authsubs, err = c.GenerateAuthSubs() + if err != nil { + return err + } + return c.Websocket.SubscribeToChannels(authsubs) } c.Websocket.SetCanUseAuthenticatedEndpoints(false) return fmt.Errorf("message: %s. code: %v", result["message"], result["code"]) @@ -141,17 +142,35 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { if err != nil { return err } + + var format currency.PairFormat + format, err = c.GetPairFormat(asset.PerpetualSwap, true) + if err != nil { + return err + } + + var pairs currency.Pairs + pairs, err = c.GetEnabledPairs(asset.PerpetualSwap) + if err != nil { + return err + } + for x := range wsTicker.Data { + var p currency.Pair + p, err = currency.NewPairFromFormattedPairs(wsTicker.Data[x].Symbol, + pairs, + format) + if err != nil { + return err + } c.Websocket.DataHandler <- &ticker.Price{ - Volume: wsTicker.Data[x].Volume24h, - Last: wsTicker.Data[x].LastPrice, - High: wsTicker.Data[x].High24h, - Low: wsTicker.Data[x].Low24h, - Bid: wsTicker.Data[x].BestBidPrice, - Ask: wsTicker.Data[x].BestAskPrice, - Pair: currency.NewPairFromFormattedPairs(wsTicker.Data[x].Symbol, - c.GetEnabledPairs(asset.PerpetualSwap), - c.GetPairFormat(asset.PerpetualSwap, true)), + Volume: wsTicker.Data[x].Volume24h, + Last: wsTicker.Data[x].LastPrice, + High: wsTicker.Data[x].High24h, + Low: wsTicker.Data[x].Low24h, + Bid: wsTicker.Data[x].BestBidPrice, + Ask: wsTicker.Data[x].BestAskPrice, + Pair: p, ExchangeName: c.Name, AssetType: asset.PerpetualSwap, LastUpdated: wsTicker.Data[x].Timestamp, @@ -182,16 +201,33 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { if tradeList.Data[0][1] == "s" { tSide = order.Sell } - c.Websocket.DataHandler <- wshandler.TradeData{ - CurrencyPair: currency.NewPairFromFormattedPairs(p, - c.GetEnabledPairs(asset.PerpetualSwap), - c.GetPairFormat(asset.PerpetualSwap, true)), - Timestamp: t, - Price: price, - Amount: amount, - Exchange: c.Name, - AssetType: asset.PerpetualSwap, - Side: tSide, + + var format currency.PairFormat + format, err = c.GetPairFormat(asset.PerpetualSwap, true) + if err != nil { + return err + } + + var pairs currency.Pairs + pairs, err = c.GetEnabledPairs(asset.PerpetualSwap) + if err != nil { + return err + } + + var newP currency.Pair + newP, err = currency.NewPairFromFormattedPairs(p, pairs, format) + if err != nil { + return err + } + + c.Websocket.DataHandler <- stream.TradeData{ + CurrencyPair: newP, + Timestamp: t, + Price: price, + Amount: amount, + Exchange: c.Name, + AssetType: asset.PerpetualSwap, + Side: tSide, } case strings.Contains(result[topic].(string), "orderBook"): orderBook := struct { @@ -209,9 +245,25 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { return err } p := strings.Replace(orderBook.Topic, "orderBook.", "", 1) - cp := currency.NewPairFromFormattedPairs(p, - c.GetEnabledPairs(asset.PerpetualSwap), - c.GetPairFormat(asset.PerpetualSwap, true)) + + var format currency.PairFormat + format, err = c.GetPairFormat(asset.PerpetualSwap, true) + if err != nil { + return err + } + + var pairs currency.Pairs + pairs, err = c.GetEnabledPairs(asset.PerpetualSwap) + if err != nil { + return err + } + + var newP currency.Pair + newP, err = currency.NewPairFromFormattedPairs(p, pairs, format) + if err != nil { + return err + } + var amount, price float64 var asks, bids []orderbook.Item for i := range orderBook.Data[0].Asks { @@ -247,23 +299,19 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { newOB.Asks = asks newOB.Bids = bids newOB.AssetType = asset.PerpetualSwap - newOB.Pair = cp + newOB.Pair = newP newOB.ExchangeName = c.Name newOB.LastUpdated = orderBook.Data[0].Timestamp err = c.Websocket.Orderbook.LoadSnapshot(&newOB) if err != nil { return err } - c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{Pair: newOB.Pair, - Asset: asset.PerpetualSwap, - Exchange: c.Name, - } } else if orderBook.Action == "update" { - newOB := wsorderbook.WebsocketOrderbookUpdate{ + newOB := buffer.Update{ Asks: asks, Bids: bids, Asset: asset.PerpetualSwap, - Pair: cp, + Pair: newP, UpdateID: orderBook.Data[0].Version, UpdateTime: orderBook.Data[0].Timestamp, } @@ -271,10 +319,6 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { if err != nil { return err } - c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{Pair: newOB.Pair, - Asset: asset.PerpetualSwap, - Exchange: c.Name, - } } case strings.Contains(result[topic].(string), "kline"): var kline WsKline @@ -291,15 +335,33 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { } tempKline = append(tempKline, tempFloat) } - p := currency.NewPairFromFormattedPairs(kline.Data[0][0].(string), - c.GetEnabledPairs(asset.PerpetualSwap), - c.GetPairFormat(asset.PerpetualSwap, true)) + + var format currency.PairFormat + format, err = c.GetPairFormat(asset.PerpetualSwap, true) + if err != nil { + return err + } + + var pairs currency.Pairs + pairs, err = c.GetEnabledPairs(asset.PerpetualSwap) + if err != nil { + return err + } + + var newP currency.Pair + newP, err = currency.NewPairFromFormattedPairs(kline.Data[0][0].(string), + pairs, + format) + if err != nil { + return err + } + if tempKline == nil && len(tempKline) < 5 { return errors.New(c.Name + " - received bad data ") } - c.Websocket.DataHandler <- wshandler.KlineData{ + c.Websocket.DataHandler <- stream.KlineData{ Timestamp: time.Unix(int64(kline.Data[0][1].(float64)), 0), - Pair: p, + Pair: newP, AssetType: asset.PerpetualSwap, Exchange: c.Name, OpenPrice: tempKline[0], @@ -328,6 +390,17 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { if err != nil { return err } + + format, err := c.GetPairFormat(asset.PerpetualSwap, true) + if err != nil { + return err + } + + pairs, err := c.GetEnabledPairs(asset.PerpetualSwap) + if err != nil { + return err + } + for i := range orders.Data { oType, err := order.StringToOrderType(orders.Data[i].OrderType) if err != nil { @@ -345,6 +418,14 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { Err: err, } } + + newP, err := currency.NewPairFromFormattedPairs(orders.Data[i].Symbol, + pairs, + format) + if err != nil { + return err + } + c.Websocket.DataHandler <- &order.Detail{ Price: orders.Data[i].OrderPrice, Amount: orders.Data[i].Quantity, @@ -358,32 +439,46 @@ func (c *Coinbene) wsHandleData(respRaw []byte) error { AssetType: asset.PerpetualSwap, Date: orders.Data[i].OrderTime, Leverage: strconv.FormatInt(orders.Data[i].Leverage, 10), - Pair: currency.NewPairFromFormattedPairs(orders.Data[i].Symbol, - c.GetEnabledPairs(asset.PerpetualSwap), - c.GetPairFormat(asset.PerpetualSwap, true)), + Pair: newP, } } default: - c.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: c.Name + wshandler.UnhandledMessage + string(respRaw)} + c.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: c.Name + stream.UnhandledMessage + string(respRaw), + } return nil } return nil } // Subscribe sends a websocket message to receive data from the channel -func (c *Coinbene) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { +func (c *Coinbene) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { var sub WsSub sub.Operation = "subscribe" - sub.Arguments = []string{channelToSubscribe.Channel} - return c.WebsocketConn.SendJSONMessage(sub) + for i := range channelsToSubscribe { + sub.Arguments = append(sub.Arguments, channelsToSubscribe[i].Channel) + } + err := c.Websocket.Conn.SendJSONMessage(sub) + if err != nil { + return err + } + c.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe...) + return nil } // Unsubscribe sends a websocket message to receive data from the channel -func (c *Coinbene) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - var sub WsSub - sub.Operation = "unsubscribe" - sub.Arguments = []string{channelToSubscribe.Channel} - return c.WebsocketConn.SendJSONMessage(sub) +func (c *Coinbene) Unsubscribe(channelToUnsubscribe []stream.ChannelSubscription) error { + var unsub WsSub + unsub.Operation = "unsubscribe" + for i := range channelToUnsubscribe { + unsub.Arguments = append(unsub.Arguments, channelToUnsubscribe[i].Channel) + } + err := c.Websocket.Conn.SendJSONMessage(unsub) + if err != nil { + return err + } + c.Websocket.RemoveSuccessfulUnsubscriptions(channelToUnsubscribe...) + return nil } // Login logs in @@ -397,5 +492,5 @@ func (c *Coinbene) Login() error { sign := crypto.HexEncodeToString(tempSign) sub.Operation = "login" sub.Arguments = []string{c.API.Credentials.Key, expTime, sign} - return c.WebsocketConn.SendJSONMessage(sub) + return c.Websocket.Conn.SendJSONMessage(sub) } diff --git a/exchanges/coinbene/coinbene_wrapper.go b/exchanges/coinbene/coinbene_wrapper.go index 9e1d0fa0..d4a931bd 100644 --- a/exchanges/coinbene/coinbene_wrapper.go +++ b/exchanges/coinbene/coinbene_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,33 +56,32 @@ func (c *Coinbene) SetDefaults() { c.API.CredentialsValidator.RequiresKey = true c.API.CredentialsValidator.RequiresSecret = true - c.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.PerpetualSwap, + err := c.StoreAssetPairFormat(asset.Spot, currency.PairStore{ + RequestFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: currency.ForwardSlashDelimiter, }, + ConfigFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: currency.ForwardSlashDelimiter, + }, + }) + if err != nil { + log.Errorln(log.ExchangeSys, err) } - c.CurrencyPairs.Store(asset.Spot, currency.PairStore{ - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "/", - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "/", - }, - }) - - c.CurrencyPairs.Store(asset.PerpetualSwap, currency.PairStore{ + err = c.StoreAssetPairFormat(asset.PerpetualSwap, currency.PairStore{ RequestFormat: ¤cy.PairFormat{ Uppercase: true, }, ConfigFormat: ¤cy.PairFormat{ Uppercase: true, - Delimiter: "/", + Delimiter: currency.ForwardSlashDelimiter, }, }) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } c.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ @@ -149,7 +148,7 @@ func (c *Coinbene) SetDefaults() { c.API.Endpoints.URLDefault = coinbeneAPIURL c.API.Endpoints.URL = c.API.Endpoints.URLDefault c.API.Endpoints.WebsocketURL = wsContractURL - c.Websocket = wshandler.New() + c.Websocket = stream.New() c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout c.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -167,42 +166,31 @@ func (c *Coinbene) Setup(exch *config.ExchangeConfig) error { return err } - err = c.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: wsContractURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: c.WsConnect, - Subscriber: c.Subscribe, - UnSubscriber: c.Unsubscribe, - Features: &c.Features.Supports.WebsocketCapabilities, - }) + err = c.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: wsContractURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: c.WsConnect, + Subscriber: c.Subscribe, + UnSubscriber: c.Unsubscribe, + GenerateSubscriptions: c.GenerateDefaultSubscriptions, + Features: &c.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + BufferEnabled: true, + SortBuffer: true, + }) if err != nil { return err } - c.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: c.Name, - URL: c.Websocket.GetWebsocketURL(), - ProxyURL: c.Websocket.GetProxyAddress(), - Verbose: c.Verbose, + return c.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - c.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - true, - true, - false, - false, - exch.Name) - - return nil + }) } // Start starts the Coinbene go routine @@ -257,6 +245,11 @@ func (c *Coinbene) FetchTradablePairs(a asset.Item) ([]string, error) { currencies = append(currencies, pairs[x].Symbol) } case asset.PerpetualSwap: + format, err := c.GetPairFormat(a, false) + if err != nil { + return nil, err + } + tickers, err := c.GetSwapTickers() if err != nil { return nil, err @@ -264,10 +257,11 @@ func (c *Coinbene) FetchTradablePairs(a asset.Item) ([]string, error) { for t := range tickers { idx := strings.Index(t, currency.USDT.String()) if idx == 0 { - return nil, fmt.Errorf("%s SWAP currency does not contain USDT", c.Name) + return nil, + fmt.Errorf("%s SWAP currency does not contain USDT", c.Name) } currencies = append(currencies, - t[0:idx]+c.GetPairFormat(a, false).Delimiter+t[idx:]) + t[0:idx]+format.Delimiter+t[idx:]) } } return currencies, nil @@ -282,8 +276,13 @@ func (c *Coinbene) UpdateTradablePairs(forceUpdate bool) error { if err != nil { return err } - err = c.UpdatePairs(currency.NewPairsFromStrings(pairs), - assets[x], false, forceUpdate) + + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + err = c.UpdatePairs(p, assets[x], false, forceUpdate) if err != nil { return err } @@ -293,30 +292,44 @@ func (c *Coinbene) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (c *Coinbene) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - resp := new(ticker.Price) if !c.SupportsAsset(assetType) { return nil, fmt.Errorf("%s does not support asset type %s", c.Name, assetType) } + allPairs, err := c.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + switch assetType { case asset.Spot: - allPairs := c.GetEnabledPairs(assetType) - for x := range allPairs { - tempResp, err := c.GetTicker(c.FormatExchangeCurrency(allPairs[x], - assetType).String()) + tickers, err := c.GetTickers() + if err != nil { + return nil, err + } + + for i := range tickers { + var newP currency.Pair + newP, err = currency.NewPairFromString(tickers[i].Symbol) if err != nil { return nil, err } - resp.Pair = allPairs[x] - resp.Last = tempResp.LatestPrice - resp.High = tempResp.DailyHigh - resp.Low = tempResp.DailyLow - resp.Bid = tempResp.BestBid - resp.Ask = tempResp.BestAsk - resp.Volume = tempResp.DailyVolume - resp.LastUpdated = time.Now() - err = ticker.ProcessTicker(c.Name, resp, assetType) + + if !allPairs.Contains(newP, true) { + continue + } + + err = ticker.ProcessTicker(&ticker.Price{ + Pair: newP, + Last: tickers[i].LatestPrice, + High: tickers[i].DailyHigh, + Low: tickers[i].DailyLow, + Bid: tickers[i].BestBid, + Ask: tickers[i].BestAsk, + Volume: tickers[i].DailyVolume, + ExchangeName: c.Name, + AssetType: assetType}) if err != nil { return nil, err } @@ -327,24 +340,31 @@ func (c *Coinbene) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker. return nil, err } - allPairs := c.GetEnabledPairs(assetType) for x := range allPairs { - tick, ok := tickers[c.FormatExchangeCurrency(allPairs[x], - assetType).String()] + fpair, err := c.FormatExchangeCurrency(allPairs[x], assetType) + if err != nil { + return nil, err + } + + tick, ok := tickers[fpair.String()] if !ok { log.Warnf(log.ExchangeSys, - "%s SWAP ticker item was not found", c.Name) + "%s SWAP ticker item was not found", + c.Name) continue } - resp.Pair = allPairs[x] - resp.Last = tick.LastPrice - resp.High = tick.High24Hour - resp.Low = tick.Low24Hour - resp.Bid = tick.BestBidPrice - resp.Ask = tick.BestAskPrice - resp.Volume = tick.Volume24Hour - resp.LastUpdated = tick.Timestamp - err = ticker.ProcessTicker(c.Name, resp, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Pair: allPairs[x], + Last: tick.LastPrice, + High: tick.High24Hour, + Low: tick.Low24Hour, + Bid: tick.BestBidPrice, + Ask: tick.BestAskPrice, + Volume: tick.Volume24Hour, + LastUpdated: tick.Timestamp, + ExchangeName: c.Name, + AssetType: assetType}) if err != nil { return nil, err } @@ -368,15 +388,15 @@ func (c *Coinbene) FetchTicker(p currency.Pair, assetType asset.Item) (*ticker.P } // FetchOrderbook returns orderbook base on the currency pair -func (c *Coinbene) FetchOrderbook(currency currency.Pair, assetType asset.Item) (*orderbook.Base, error) { +func (c *Coinbene) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { if !c.SupportsAsset(assetType) { return nil, fmt.Errorf("%s does not support asset type %s", c.Name, assetType) } - ob, err := orderbook.Get(c.Name, currency, assetType) + ob, err := orderbook.Get(c.Name, p, assetType) if err != nil { - return c.UpdateOrderbook(currency, assetType) + return c.UpdateOrderbook(p, assetType) } return ob, nil } @@ -389,18 +409,19 @@ func (c *Coinbene) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orde fmt.Errorf("%s does not support asset type %s", c.Name, assetType) } - var tempResp Orderbook - var err error + fpair, err := c.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + var tempResp Orderbook switch assetType { case asset.Spot: - tempResp, err = c.GetOrderbook( - c.FormatExchangeCurrency(p, assetType).String(), + tempResp, err = c.GetOrderbook(fpair.String(), 100, // TO-DO: Update this once we support configurable orderbook depth ) case asset.PerpetualSwap: - tempResp, err = c.GetSwapOrderbook( - c.FormatExchangeCurrency(p, assetType).String(), + tempResp, err = c.GetSwapOrderbook(fpair.String(), 100, // TO-DO: Update this once we support configurable orderbook depth ) } @@ -501,13 +522,15 @@ func (c *Coinbene) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { fmt.Errorf("%s orderside is not supported by this exchange", s.Side) } - if s.Type != order.Limit { - return resp, fmt.Errorf("only limit order is supported by this exchange") + + fpair, err := c.FormatExchangeCurrency(s.Pair, asset.Spot) + if err != nil { + return resp, err } tempResp, err := c.PlaceSpotOrder(s.Price, s.Amount, - c.FormatExchangeCurrency(s.Pair, asset.Spot).String(), + fpair.String(), s.Side.String(), s.Type.String(), s.ClientID, @@ -535,13 +558,17 @@ func (c *Coinbene) CancelOrder(order *order.Cancel) error { // CancelAllOrders cancels all orders associated with a currency pair func (c *Coinbene) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) { var resp order.CancelAllResponse - orders, err := c.FetchOpenSpotOrders( - c.FormatExchangeCurrency(orderCancellation.Pair, - asset.Spot).String(), - ) + fpair, err := c.FormatExchangeCurrency(orderCancellation.Pair, + orderCancellation.AssetType) if err != nil { return resp, err } + + orders, err := c.FetchOpenSpotOrders(fpair.String()) + if err != nil { + return resp, err + } + tempMap := make(map[string]string) for x := range orders { _, err := c.CancelSpotOrder(orders[x].OrderID) @@ -597,11 +624,6 @@ func (c *Coinbene) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdra return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (c *Coinbene) GetWebsocket() (*wshandler.Websocket, error) { - return c.Websocket, nil -} - // GetActiveOrders retrieves any orders that are active/open func (c *Coinbene) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) { if len(getOrdersRequest.Pairs) == 0 { @@ -610,21 +632,24 @@ func (c *Coinbene) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([] return nil, err } for a := range allPairs { - getOrdersRequest.Pairs = append(getOrdersRequest.Pairs, - currency.NewPairFromString(allPairs[a].Symbol)) + p, err := currency.NewPairFromString(allPairs[a].Symbol) + if err != nil { + return nil, err + } + getOrdersRequest.Pairs = append(getOrdersRequest.Pairs, p) } } - var err error var resp []order.Detail - for x := range getOrdersRequest.Pairs { + fpair, err := c.FormatExchangeCurrency(getOrdersRequest.Pairs[x], + asset.Spot) + if err != nil { + return nil, err + } + var tempData OrdersInfo - tempData, err = c.FetchOpenSpotOrders( - c.FormatExchangeCurrency( - getOrdersRequest.Pairs[x], - asset.Spot).String(), - ) + tempData, err = c.FetchOpenSpotOrders(fpair.String()) if err != nil { return nil, err } @@ -658,23 +683,26 @@ func (c *Coinbene) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([] if err != nil { return nil, err } + for a := range allPairs { - getOrdersRequest.Pairs = append(getOrdersRequest.Pairs, - currency.NewPairFromString(allPairs[a].Symbol)) + p, err := currency.NewPairFromString(allPairs[a].Symbol) + if err != nil { + return nil, err + } + getOrdersRequest.Pairs = append(getOrdersRequest.Pairs, p) } } var resp []order.Detail var tempData OrdersInfo - var err error - for x := range getOrdersRequest.Pairs { - tempData, err = c.FetchClosedOrders( - c.FormatExchangeCurrency( - getOrdersRequest.Pairs[x], - asset.Spot).String(), - "", - ) + fpair, err := c.FormatExchangeCurrency(getOrdersRequest.Pairs[x], + asset.Spot) + if err != nil { + return nil, err + } + + tempData, err = c.FetchClosedOrders(fpair.String(), "") if err != nil { return nil, err } @@ -702,40 +730,20 @@ func (c *Coinbene) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([] // GetFeeByType returns an estimate of fee based on the type of transaction func (c *Coinbene) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { - var fee float64 - tempData, err := c.GetPairInfo( - c.FormatExchangeCurrency( - feeBuilder.Pair, asset.Spot).String(), - ) + fpair, err := c.FormatExchangeCurrency(feeBuilder.Pair, asset.Spot) if err != nil { - return fee, err + return 0, err } - switch feeBuilder.IsMaker { - case true: - fee = feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.MakerFeeRate - case false: - fee = feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.TakerFeeRate + + tempData, err := c.GetPairInfo(fpair.String()) + if err != nil { + return 0, err } - return fee, nil -} -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (c *Coinbene) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - c.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (c *Coinbene) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - c.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (c *Coinbene) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return c.Websocket.GetSubscriptions(), nil + if feeBuilder.IsMaker { + return feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.MakerFeeRate, nil + } + return feeBuilder.PurchasePrice * feeBuilder.Amount * tempData.TakerFeeRate, nil } // AuthenticateWebsocket sends an authentication message to the websocket @@ -774,14 +782,18 @@ func (c *Coinbene) GetHistoricCandles(pair currency.Pair, a asset.Item, start, e } } + formattedPair, err := c.FormatExchangeCurrency(pair, asset.PerpetualSwap) + if err != nil { + return kline.Item{}, err + } + var candles CandleResponse - var err error if a == asset.PerpetualSwap { - candles, err = c.GetSwapKlines(c.FormatExchangeCurrency(pair, asset.PerpetualSwap).String(), + candles, err = c.GetSwapKlines(formattedPair.String(), start, end, c.FormatExchangeKlineInterval(interval)) } else { - candles, err = c.GetKlines(c.FormatExchangeCurrency(pair, asset.Spot).String(), + candles, err = c.GetKlines(formattedPair.String(), start, end, c.FormatExchangeKlineInterval(interval)) } diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index 9c77dde1..c4e6916f 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -17,7 +17,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -42,6 +41,8 @@ const ( coinutStatusOK = "OK" coinutMaxNonce = 16777215 // See https://github.com/coinut/api/wiki/Websocket-API#nonce + + wsRateLimitInMilliseconds = 33 ) var ( @@ -52,7 +53,6 @@ var ( // COINUT is the overarching type across the coinut package type COINUT struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection instrumentMap instrumentMap } diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index 6e95e983..b1f31fb0 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -14,7 +14,7 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -43,6 +43,7 @@ func TestMain(m *testing.M) { coinutCfg.API.AuthenticatedWebsocketSupport = true coinutCfg.API.Credentials.Key = apiKey coinutCfg.API.Credentials.ClientID = clientID + c.Websocket = sharedtestvalues.NewTestWebsocket() err = c.Setup(coinutCfg) if err != nil { log.Fatal("Coinut setup error", err) @@ -51,8 +52,6 @@ func TestMain(m *testing.M) { if err != nil { log.Fatal("Coinut setup error ", err) } - c.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - c.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -62,27 +61,17 @@ func setupWSTestAuth(t *testing.T) { } if !c.Websocket.IsEnabled() && !c.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) + t.Skip(stream.WebsocketNotEnabled) } if areTestAPIKeysSet() { c.Websocket.SetCanUseAuthenticatedEndpoints(true) } - c.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: c.Name, - URL: coinutWebsocketURL, - Verbose: c.Verbose, - RateLimit: coinutWebsocketRateLimit, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, - } var dialer websocket.Dialer - err := c.WebsocketConn.Dial(&dialer, http.Header{}) + err := c.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } - c.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - c.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() go c.wsReadData() err = c.wsAuthenticate() if err != nil { diff --git a/exchanges/coinut/coinut_websocket.go b/exchanges/coinut/coinut_websocket.go index bd271d12..f7bc0740 100644 --- a/exchanges/coinut/coinut_websocket.go +++ b/exchanges/coinut/coinut_websocket.go @@ -10,15 +10,16 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -39,10 +40,10 @@ var ( // WsConnect intiates a websocket connection func (c *COINUT) WsConnect() error { if !c.Websocket.IsEnabled() || !c.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := c.WebsocketConn.Dial(&dialer, http.Header{}) + err := c.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } @@ -59,7 +60,15 @@ func (c *COINUT) WsConnect() error { c.Websocket.SetCanUseAuthenticatedEndpoints(false) log.Error(log.WebsocketMgr, err) } - c.GenerateDefaultSubscriptions() + subs, err := c.GenerateDefaultSubscriptions() + if err != nil { + return err + } + + err = c.Websocket.SubscribeToChannels(subs) + if err != nil { + return err + } // define bi-directional communication channels = make(map[string]chan []byte) @@ -71,61 +80,49 @@ func (c *COINUT) WsConnect() error { // wsReadData receives and passes on websocket messages for processing func (c *COINUT) wsReadData() { c.Websocket.Wg.Add(1) - - defer func() { - c.Websocket.Wg.Done() - }() + defer c.Websocket.Wg.Done() for { - select { - case <-c.Websocket.ShutdownC: + resp := c.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return + } - default: - resp, err := c.WebsocketConn.ReadMessage() + if strings.HasPrefix(string(resp.Raw), "[") { + var incoming []wsResponse + err := json.Unmarshal(resp.Raw, &incoming) if err != nil { - c.Websocket.ReadMessageErrors <- err - return + c.Websocket.DataHandler <- err + continue } - c.Websocket.TrafficAlert <- struct{}{} - - if strings.HasPrefix(string(resp.Raw), "[") { - var incoming []wsResponse - err = json.Unmarshal(resp.Raw, &incoming) + for i := range incoming { + if incoming[i].Nonce > 0 { + if c.Websocket.Match.IncomingWithData(incoming[i].Nonce, resp.Raw) { + break + } + } + var individualJSON []byte + individualJSON, err = json.Marshal(incoming[i]) if err != nil { c.Websocket.DataHandler <- err continue } - for i := range incoming { - if incoming[i].Nonce > 0 { - if c.WebsocketConn.IsIDWaitingForResponse(incoming[i].Nonce) { - c.WebsocketConn.SetResponseIDAndData(incoming[i].Nonce, resp.Raw) - break - } - } - var individualJSON []byte - individualJSON, err = json.Marshal(incoming[i]) - if err != nil { - c.Websocket.DataHandler <- err - continue - } - err = c.wsHandleData(individualJSON) - if err != nil { - c.Websocket.DataHandler <- err - } - } - } else { - var incoming wsResponse - err = json.Unmarshal(resp.Raw, &incoming) - if err != nil { - c.Websocket.DataHandler <- err - continue - } - err = c.wsHandleData(resp.Raw) + err = c.wsHandleData(individualJSON) if err != nil { c.Websocket.DataHandler <- err } } + } else { + var incoming wsResponse + err := json.Unmarshal(resp.Raw, &incoming) + if err != nil { + c.Websocket.DataHandler <- err + continue + } + err = c.wsHandleData(resp.Raw) + if err != nil { + c.Websocket.DataHandler <- err + } } } } @@ -153,11 +150,16 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { return err } if strings.Contains(string(respRaw), "client_ord_id") { - if c.WebsocketConn.IsIDWaitingForResponse(incoming.Nonce) { - c.WebsocketConn.SetResponseIDAndData(incoming.Nonce, respRaw) + if c.Websocket.Match.IncomingWithData(incoming.Nonce, respRaw) { return nil } } + + format, err := c.GetPairFormat(asset.Spot, true) + if err != nil { + return err + } + switch incoming.Reply { case "hb": channels["hb"] <- respRaw @@ -167,9 +169,16 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { if err != nil { return err } + + var endpointFailure []byte if login.APIKey != c.API.Credentials.Key { - c.API.AuthenticatedWebsocketSupport = false + endpointFailure = []byte("failed to authenticate") } + + if c.Websocket.Match.IncomingWithData(login.Nonce, endpointFailure) { + return nil + } + case "user_balance": var userBalance WsUserBalanceResponse err := json.Unmarshal(respRaw, &userBalance) @@ -231,7 +240,18 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { if err != nil { return err } + pairs, err := c.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } currencyPair := c.instrumentMap.LookupInstrument(wsTicker.InstID) + p, err := currency.NewPairFromFormattedPairs(currencyPair, + pairs, + format) + if err != nil { + return err + } + c.Websocket.DataHandler <- &ticker.Price{ ExchangeName: c.Name, Volume: wsTicker.Volume24, @@ -243,9 +263,7 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { Last: wsTicker.Last, LastUpdated: time.Unix(0, wsTicker.Timestamp), AssetType: asset.Spot, - Pair: currency.NewPairFromFormattedPairs(currencyPair, - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true)), + Pair: p, } case "inst_order_book": var orderbookSnapshot WsOrderbookSnapshot @@ -257,14 +275,6 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { if err != nil { return err } - currencyPair := c.instrumentMap.LookupInstrument(orderbookSnapshot.InstID) - c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: c.Name, - Asset: asset.Spot, - Pair: currency.NewPairFromFormattedPairs(currencyPair, - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true)), - } case "inst_order_book_update": var orderbookUpdate WsOrderbookUpdate err := json.Unmarshal(respRaw, &orderbookUpdate) @@ -275,14 +285,6 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { if err != nil { return err } - currencyPair := c.instrumentMap.LookupInstrument(orderbookUpdate.InstID) - c.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: c.Name, - Asset: asset.Spot, - Pair: currency.NewPairFromFormattedPairs(currencyPair, - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true)), - } case "inst_trade": var tradeSnap WsTradeSnapshot err := json.Unmarshal(respRaw, &tradeSnap) @@ -296,7 +298,19 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { if err != nil { return err } + + pairs, err := c.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } currencyPair := c.instrumentMap.LookupInstrument(tradeUpdate.InstID) + p, err := currency.NewPairFromFormattedPairs(currencyPair, + pairs, + format) + if err != nil { + return err + } + tSide, err := order.StringToOrderSide(tradeUpdate.Side) if err != nil { c.Websocket.DataHandler <- order.ClassificationError{ @@ -304,15 +318,14 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { Err: err, } } - c.Websocket.DataHandler <- wshandler.TradeData{ - Timestamp: time.Unix(tradeUpdate.Timestamp, 0), - CurrencyPair: currency.NewPairFromFormattedPairs(currencyPair, - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true)), - AssetType: asset.Spot, - Exchange: c.Name, - Price: tradeUpdate.Price, - Side: tSide, + + c.Websocket.DataHandler <- stream.TradeData{ + Timestamp: time.Unix(tradeUpdate.Timestamp, 0), + CurrencyPair: p, + AssetType: asset.Spot, + Exchange: c.Name, + Price: tradeUpdate.Price, + Side: tSide, } case "order_filled", "order_rejected", "order_accepted": var orderContainer wsOrderContainer @@ -326,7 +339,7 @@ func (c *COINUT) wsHandleData(respRaw []byte) error { } c.Websocket.DataHandler <- o default: - c.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: c.Name + wshandler.UnhandledMessage + string(respRaw)} + c.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: c.Name + stream.UnhandledMessage + string(respRaw)} return nil } return nil @@ -444,7 +457,7 @@ func (c *COINUT) WsGetInstruments() (Instruments, error) { SecurityType: strings.ToUpper(asset.Spot.String()), Nonce: getNonce(), } - resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(request.Nonce, request) if err != nil { return list, err } @@ -482,11 +495,25 @@ func (c *COINUT) WsProcessOrderbookSnapshot(ob *WsOrderbookSnapshot) error { var newOrderBook orderbook.Base newOrderBook.Asks = asks newOrderBook.Bids = bids - newOrderBook.Pair = currency.NewPairFromFormattedPairs( + + pairs, err := c.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + + format, err := c.GetPairFormat(asset.Spot, true) + if err != nil { + return err + } + + newOrderBook.Pair, err = currency.NewPairFromFormattedPairs( c.instrumentMap.LookupInstrument(ob.InstID), - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true), - ) + pairs, + format) + if err != nil { + return err + } + newOrderBook.AssetType = asset.Spot newOrderBook.ExchangeName = c.Name @@ -495,12 +522,25 @@ func (c *COINUT) WsProcessOrderbookSnapshot(ob *WsOrderbookSnapshot) error { // WsProcessOrderbookUpdate process an orderbook update func (c *COINUT) WsProcessOrderbookUpdate(update *WsOrderbookUpdate) error { - p := currency.NewPairFromFormattedPairs( + pairs, err := c.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + + format, err := c.GetPairFormat(asset.Spot, true) + if err != nil { + return err + } + + p, err := currency.NewPairFromFormattedPairs( c.instrumentMap.LookupInstrument(update.InstID), - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true), - ) - bufferUpdate := &wsorderbook.WebsocketOrderbookUpdate{ + pairs, + format) + if err != nil { + return err + } + + bufferUpdate := &buffer.Update{ Pair: p, UpdateID: update.TransID, Asset: asset.Spot, @@ -514,67 +554,109 @@ func (c *COINUT) WsProcessOrderbookUpdate(update *WsOrderbookUpdate) error { } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (c *COINUT) GenerateDefaultSubscriptions() { +func (c *COINUT) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { var channels = []string{"inst_tick", "inst_order_book"} - var subscriptions []wshandler.WebsocketChannelSubscription - enabledCurrencies := c.GetEnabledPairs(asset.Spot) + var subscriptions []stream.ChannelSubscription + enabledCurrencies, err := c.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } for i := range channels { for j := range enabledCurrencies { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channels[i], Currency: enabledCurrencies[j], + Asset: asset.Spot, }) } } - c.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (c *COINUT) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - subscribe := wsRequest{ - Request: channelToSubscribe.Channel, - InstrumentID: c.instrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String()), - Subscribe: true, - Nonce: getNonce(), +func (c *COINUT) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + fpair, err := c.FormatExchangeCurrency(channelsToSubscribe[i].Currency, asset.Spot) + if err != nil { + errs = append(errs, err) + continue + } + + subscribe := wsRequest{ + Request: channelsToSubscribe[i].Channel, + InstrumentID: c.instrumentMap.LookupID(fpair.String()), + Subscribe: true, + Nonce: getNonce(), + } + err = c.Websocket.Conn.SendJSONMessage(subscribe) + if err != nil { + errs = append(errs, err) + continue + } + c.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) } - return c.WebsocketConn.SendJSONMessage(subscribe) + if errs != nil { + return errs + } + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (c *COINUT) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - subscribe := wsRequest{ - Request: channelToSubscribe.Channel, - InstrumentID: c.instrumentMap.LookupID(c.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String()), - Subscribe: false, - Nonce: getNonce(), +func (c *COINUT) Unsubscribe(channelToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelToUnsubscribe { + fpair, err := c.FormatExchangeCurrency(channelToUnsubscribe[i].Currency, asset.Spot) + if err != nil { + errs = append(errs, err) + continue + } + + subscribe := wsRequest{ + Request: channelToUnsubscribe[i].Channel, + InstrumentID: c.instrumentMap.LookupID(fpair.String()), + Subscribe: false, + Nonce: getNonce(), + } + resp, err := c.Websocket.Conn.SendMessageReturnResponse(subscribe.Nonce, + subscribe) + if err != nil { + errs = append(errs, err) + continue + } + var response map[string]interface{} + err = json.Unmarshal(resp, &response) + if err != nil { + errs = append(errs, err) + continue + } + if response["status"].([]interface{})[0] != "OK" { + errs = append(errs, fmt.Errorf("%v unsubscribe failed for channel %v", + c.Name, + channelToUnsubscribe[i].Channel)) + continue + } + c.Websocket.RemoveSuccessfulUnsubscriptions(channelToUnsubscribe[i]) } - resp, err := c.WebsocketConn.SendMessageReturnResponse(subscribe.Nonce, subscribe) - if err != nil { - return err - } - var response map[string]interface{} - err = json.Unmarshal(resp, &response) - if err != nil { - return err - } - if response["status"].([]interface{})[0] != "OK" { - return fmt.Errorf("%v unsubscribe failed for channel %v", c.Name, channelToSubscribe.Channel) + if errs != nil { + return errs } return nil } func (c *COINUT) wsAuthenticate() error { if !c.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", c.Name) + return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", + c.Name) } timestamp := time.Now().Unix() nonce := getNonce() payload := c.API.Credentials.ClientID + "|" + strconv.FormatInt(timestamp, 10) + "|" + strconv.FormatInt(nonce, 10) - hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(payload), []byte(c.API.Credentials.Key)) + hmac := crypto.GetHMAC(crypto.HashSHA256, + []byte(payload), + []byte(c.API.Credentials.Key)) loginRequest := struct { Request string `json:"request"` Username string `json:"username"` @@ -589,18 +671,14 @@ func (c *COINUT) wsAuthenticate() error { Timestamp: timestamp, } - resp, err := c.WebsocketConn.SendMessageReturnResponse(loginRequest.Nonce, loginRequest) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(loginRequest.Nonce, + loginRequest) if err != nil { return err } - var response map[string]interface{} - err = json.Unmarshal(resp, &response) - if err != nil { - return err - } - if response["status"].([]interface{})[0] != "OK" { + if resp != nil { c.Websocket.SetCanUseAuthenticatedEndpoints(false) - return fmt.Errorf("%v failed to authenticate", c.Name) + return fmt.Errorf("%v %s", c.Name, resp) } c.Websocket.SetCanUseAuthenticatedEndpoints(true) return nil @@ -614,7 +692,8 @@ func (c *COINUT) wsGetAccountBalance() (*UserBalance, error) { Request: "user_balance", Nonce: getNonce(), } - resp, err := c.WebsocketConn.SendMessageReturnResponse(accBalance.Nonce, accBalance) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(accBalance.Nonce, + accBalance) if err != nil { return nil, err } @@ -633,11 +712,16 @@ func (c *COINUT) wsSubmitOrder(o *WsSubmitOrderParameters) (*order.Detail, error if !c.Websocket.CanUseAuthenticatedEndpoints() { return nil, fmt.Errorf("%v not authorised to submit order", c.Name) } - curr := c.FormatExchangeCurrency(o.Currency, asset.Spot).String() + + curr, err := c.FormatExchangeCurrency(o.Currency, asset.Spot) + if err != nil { + return nil, err + } + var orderSubmissionRequest WsSubmitOrderRequest orderSubmissionRequest.Request = "new_order" orderSubmissionRequest.Nonce = getNonce() - orderSubmissionRequest.InstrumentID = c.instrumentMap.LookupID(curr) + orderSubmissionRequest.InstrumentID = c.instrumentMap.LookupID(curr.String()) orderSubmissionRequest.Quantity = o.Amount orderSubmissionRequest.Price = o.Price orderSubmissionRequest.Side = string(o.Side) @@ -645,7 +729,8 @@ func (c *COINUT) wsSubmitOrder(o *WsSubmitOrderParameters) (*order.Detail, error if o.OrderID > 0 { orderSubmissionRequest.OrderID = o.OrderID } - resp, err := c.WebsocketConn.SendMessageReturnResponse(orderSubmissionRequest.Nonce, orderSubmissionRequest) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(orderSubmissionRequest.Nonce, + orderSubmissionRequest) if err != nil { return nil, err } @@ -666,25 +751,31 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]order.Detai var errs []error var ordersResponse []order.Detail if !c.Websocket.CanUseAuthenticatedEndpoints() { - errs = append(errs, fmt.Errorf("%v not authorised to submit orders", c.Name)) + errs = append(errs, fmt.Errorf("%v not authorised to submit orders", + c.Name)) return nil, errs } orderRequest := WsSubmitOrdersRequest{} for i := range orders { - curr := c.FormatExchangeCurrency(orders[i].Currency, asset.Spot).String() + curr, err := c.FormatExchangeCurrency(orders[i].Currency, asset.Spot) + if err != nil { + return nil, []error{err} + } + orderRequest.Orders = append(orderRequest.Orders, WsSubmitOrdersRequestData{ Quantity: orders[i].Amount, Price: orders[i].Price, Side: string(orders[i].Side), - InstrumentID: c.instrumentMap.LookupID(curr), + InstrumentID: c.instrumentMap.LookupID(curr.String()), ClientOrderID: i + 1, }) } orderRequest.Nonce = getNonce() orderRequest.Request = "new_orders" - resp, err := c.WebsocketConn.SendMessageReturnResponse(orderRequest.Nonce, orderRequest) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(orderRequest.Nonce, + orderRequest) if err != nil { errs = append(errs, err) return nil, errs @@ -710,14 +801,16 @@ func (c *COINUT) wsSubmitOrders(orders []WsSubmitOrderParameters) ([]order.Detai func (c *COINUT) wsGetOpenOrders(curr string) (*WsUserOpenOrdersResponse, error) { var response *WsUserOpenOrdersResponse if !c.Websocket.CanUseAuthenticatedEndpoints() { - return response, fmt.Errorf("%v not authorised to get open orders", c.Name) + return response, fmt.Errorf("%v not authorised to get open orders", + c.Name) } var openOrdersRequest WsGetOpenOrdersRequest openOrdersRequest.Request = "user_open_orders" openOrdersRequest.Nonce = getNonce() openOrdersRequest.InstrumentID = c.instrumentMap.LookupID(curr) - resp, err := c.WebsocketConn.SendMessageReturnResponse(openOrdersRequest.Nonce, openOrdersRequest) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(openOrdersRequest.Nonce, + openOrdersRequest) if err != nil { return response, err } @@ -738,14 +831,20 @@ func (c *COINUT) wsCancelOrder(cancellation *WsCancelOrderParameters) (*CancelOr if !c.Websocket.CanUseAuthenticatedEndpoints() { return response, fmt.Errorf("%v not authorised to cancel order", c.Name) } - curr := c.FormatExchangeCurrency(cancellation.Currency, asset.Spot).String() + + curr, err := c.FormatExchangeCurrency(cancellation.Currency, asset.Spot) + if err != nil { + return nil, err + } + var cancellationRequest WsCancelOrderRequest cancellationRequest.Request = "cancel_order" - cancellationRequest.InstrumentID = c.instrumentMap.LookupID(curr) + cancellationRequest.InstrumentID = c.instrumentMap.LookupID(curr.String()) cancellationRequest.OrderID = cancellation.OrderID cancellationRequest.Nonce = getNonce() - resp, err := c.WebsocketConn.SendMessageReturnResponse(cancellationRequest.Nonce, cancellationRequest) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(cancellationRequest.Nonce, + cancellationRequest) if err != nil { return response, err } @@ -771,16 +870,23 @@ func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*Cance } var cancelOrderRequest WsCancelOrdersRequest for i := range cancellations { - curr := c.FormatExchangeCurrency(cancellations[i].Currency, asset.Spot).String() - cancelOrderRequest.Entries = append(cancelOrderRequest.Entries, WsCancelOrdersRequestEntry{ - InstID: c.instrumentMap.LookupID(curr), - OrderID: cancellations[i].OrderID, - }) + var curr currency.Pair + curr, err = c.FormatExchangeCurrency(cancellations[i].Currency, + asset.Spot) + if err != nil { + return nil, err + } + cancelOrderRequest.Entries = append(cancelOrderRequest.Entries, + WsCancelOrdersRequestEntry{ + InstID: c.instrumentMap.LookupID(curr.String()), + OrderID: cancellations[i].OrderID, + }) } cancelOrderRequest.Request = "cancel_orders" cancelOrderRequest.Nonce = getNonce() - resp, err := c.WebsocketConn.SendMessageReturnResponse(cancelOrderRequest.Nonce, cancelOrderRequest) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(cancelOrderRequest.Nonce, + cancelOrderRequest) if err != nil { return response, err } @@ -794,17 +900,24 @@ func (c *COINUT) wsCancelOrders(cancellations []WsCancelOrderParameters) (*Cance func (c *COINUT) wsGetTradeHistory(p currency.Pair, start, limit int64) (*WsTradeHistoryResponse, error) { var response *WsTradeHistoryResponse if !c.Websocket.CanUseAuthenticatedEndpoints() { - return response, fmt.Errorf("%v not authorised to get trade history", c.Name) + return response, fmt.Errorf("%v not authorised to get trade history", + c.Name) } - curr := c.FormatExchangeCurrency(p, asset.Spot).String() + + curr, err := c.FormatExchangeCurrency(p, asset.Spot) + if err != nil { + return nil, err + } + var request WsTradeHistoryRequest request.Request = "trade_history" - request.InstID = c.instrumentMap.LookupID(curr) + request.InstID = c.instrumentMap.LookupID(curr.String()) request.Nonce = getNonce() request.Start = start request.Limit = limit - resp, err := c.WebsocketConn.SendMessageReturnResponse(request.Nonce, request) + resp, err := c.Websocket.Conn.SendMessageReturnResponse(request.Nonce, + request) if err != nil { return response, err } diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index fed59a71..51b3b59a 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -20,8 +20,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -57,18 +57,11 @@ func (c *COINUT) SetDefaults() { c.API.CredentialsValidator.RequiresKey = true c.API.CredentialsValidator.RequiresClientID = true - c.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "-", - }, + requestFmt := ¤cy.PairFormat{Uppercase: true} + configFmt := ¤cy.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter} + err := c.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } c.Features = exchange.Features{ @@ -122,7 +115,7 @@ func (c *COINUT) SetDefaults() { c.API.Endpoints.URLDefault = coinutAPIURL c.API.Endpoints.URL = c.API.Endpoints.URLDefault c.API.Endpoints.WebsocketURL = coinutWebsocketURL - c.Websocket = wshandler.New() + c.Websocket = stream.New() c.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit c.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout c.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -141,41 +134,33 @@ func (c *COINUT) Setup(exch *config.ExchangeConfig) error { return err } - err = c.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: coinutWebsocketURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: c.WsConnect, - Subscriber: c.Subscribe, - UnSubscriber: c.Unsubscribe, - Features: &c.Features.Supports.WebsocketCapabilities, - }) + err = c.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: coinutWebsocketURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: c.WsConnect, + Subscriber: c.Subscribe, + UnSubscriber: c.Unsubscribe, + GenerateSubscriptions: c.GenerateDefaultSubscriptions, + Features: &c.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + BufferEnabled: true, + SortBuffer: true, + SortBufferByUpdateIDs: true, + }) if err != nil { return err } - c.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: c.Name, - URL: c.Websocket.GetWebsocketURL(), - ProxyURL: c.Websocket.GetProxyAddress(), - Verbose: c.Verbose, + return c.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - c.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - true, - true, - true, - false, - exch.Name) - return nil + RateLimit: wsRateLimitInMilliseconds, + }) } // Start starts the COINUT go routine @@ -195,21 +180,55 @@ func (c *COINUT) Run() { } forceUpdate := false - delim := c.GetPairFormat(asset.Spot, false).Delimiter - if !common.StringDataContains(c.CurrencyPairs.GetPairs(asset.Spot, - true).Strings(), delim) || - !common.StringDataContains(c.CurrencyPairs.GetPairs(asset.Spot, - false).Strings(), delim) { - enabledPairs := currency.NewPairsFromStrings( - []string{currency.LTC.String() + delim + currency.USDT.String()}, - ) - log.Warn(log.ExchangeSys, - "Enabled pairs for Coinut reset due to config upgrade, please enable the ones you would like to use again") - forceUpdate = true + format, err := c.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } - err := c.UpdatePairs(enabledPairs, asset.Spot, true, true) + enabled, err := c.CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } + avail, err := c.CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.LTC.String() + + format.Delimiter + + currency.USDT.String()}) if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", c.Name, err) + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + } else { + log.Warn(log.ExchangeSys, + "Enabled pairs for Coinut reset due to config upgrade, please enable the ones you would like to use again") + forceUpdate = true + + err = c.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + c.Name, + err) + } } } @@ -217,7 +236,7 @@ func (c *COINUT) Run() { return } - err := c.UpdateTradablePairs(forceUpdate) + err = c.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", c.Name, err) } @@ -239,11 +258,17 @@ func (c *COINUT) FetchTradablePairs(asset asset.Item) ([]string, error) { return nil, err } } + + format, err := c.GetPairFormat(asset, false) + if err != nil { + return nil, err + } + instruments = resp.Instruments var pairs []string for i := range instruments { c.instrumentMap.Seed(instruments[i][0].Base+instruments[i][0].Quote, instruments[i][0].InstrumentID) - p := instruments[i][0].Base + c.GetPairFormat(asset, false).Delimiter + instruments[i][0].Quote + p := instruments[i][0].Base + format.Delimiter + instruments[i][0].Quote pairs = append(pairs, p) } @@ -258,8 +283,11 @@ func (c *COINUT) UpdateTradablePairs(forceUpdate bool) error { return err } - return c.UpdatePairs(currency.NewPairsFromStrings(pairs), - asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return c.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateAccountInfo retrieves balances for all enabled currencies for the @@ -365,35 +393,39 @@ func (c *COINUT) FetchAccountInfo() (account.Holdings, error) { // UpdateTicker updates and returns the ticker for a currency pair func (c *COINUT) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) err := c.loadInstrumentsIfNotLoaded() if err != nil { - return tickerPrice, err + return nil, err } - instID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(p, - assetType).String()) + fpair, err := c.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + + instID := c.instrumentMap.LookupID(fpair.String()) if instID == 0 { - return tickerPrice, errors.New("unable to lookup instrument ID") + return nil, errors.New("unable to lookup instrument ID") } var tick Ticker tick, err = c.GetInstrumentTicker(instID) if err != nil { - return tickerPrice, err + return nil, err } - tickerPrice = &ticker.Price{ - Last: tick.Last, - High: tick.High24, - Low: tick.Low24, - Bid: tick.HighestBuy, - Ask: tick.LowestSell, - Volume: tick.Volume24, - Pair: p, - LastUpdated: time.Unix(0, tick.Timestamp), - } - err = ticker.ProcessTicker(c.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick.Last, + High: tick.High24, + Low: tick.Low24, + Bid: tick.HighestBuy, + Ask: tick.LowestSell, + Volume: tick.Volume24, + Pair: p, + LastUpdated: time.Unix(0, tick.Timestamp), + ExchangeName: c.Name, + AssetType: assetType}) if err != nil { - return tickerPrice, err + return nil, err } return ticker.GetTicker(c.Name, p, assetType) @@ -425,8 +457,12 @@ func (c *COINUT) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderb return orderBook, err } - instID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(p, - assetType).String()) + fpair, err := c.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + + instID := c.instrumentMap.LookupID(fpair.String()) if instID == 0 { return orderBook, errLookupInstrumentID } @@ -499,8 +535,12 @@ func (c *COINUT) SubmitOrder(o *order.Submit) (order.SubmitResponse, error) { return submitOrderResponse, err } - currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency(o.Pair, - asset.Spot).String()) + fpair, err := c.FormatExchangeCurrency(o.Pair, asset.Spot) + if err != nil { + return submitOrderResponse, err + } + + currencyID := c.instrumentMap.LookupID(fpair.String()) if currencyID == 0 { return submitOrderResponse, errLookupInstrumentID } @@ -555,10 +595,13 @@ func (c *COINUT) CancelOrder(o *order.Cancel) error { return err } - currencyID := c.instrumentMap.LookupID(c.FormatExchangeCurrency( - o.Pair, - asset.Spot).String(), - ) + fpair, err := c.FormatExchangeCurrency(o.Pair, asset.Spot) + if err != nil { + return err + } + + currencyID := c.instrumentMap.LookupID(fpair.String()) + if c.Websocket.CanUseAuthenticatedWebsocketForWrapper() { var resp *CancelOrdersResponse resp, err = c.wsCancelOrder(&WsCancelOrderParameters{ @@ -599,7 +642,12 @@ func (c *COINUT) CancelAllOrders(details *order.Cancel) (order.CancelAllResponse } var ordersToCancel []WsCancelOrderParameters for i := range openOrders.Orders { - if openOrders.Orders[i].InstrumentID == c.instrumentMap.LookupID(c.FormatExchangeCurrency(details.Pair, asset.Spot).String()) { + var fpair currency.Pair + fpair, err = c.FormatExchangeCurrency(details.Pair, asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } + if openOrders.Orders[i].InstrumentID == c.instrumentMap.LookupID(fpair.String()) { ordersToCancel = append(ordersToCancel, WsCancelOrderParameters{ Currency: details.Pair, OrderID: openOrders.Orders[i].OrderID, @@ -619,7 +667,11 @@ func (c *COINUT) CancelAllOrders(details *order.Cancel) (order.CancelAllResponse var allTheOrders []OrderResponse ids := c.instrumentMap.GetInstrumentIDs() for x := range ids { - if ids[x] == c.instrumentMap.LookupID(c.FormatExchangeCurrency(details.Pair, asset.Spot).String()) { + fpair, err := c.FormatExchangeCurrency(details.Pair, asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } + if ids[x] == c.instrumentMap.LookupID(fpair.String()) { openOrders, err := c.GetOpenOrders(ids[x]) if err != nil { return cancelAllOrdersResponse, err @@ -682,11 +734,6 @@ func (c *COINUT) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw. return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (c *COINUT) GetWebsocket() (*wshandler.Websocket, error) { - return c.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (c *COINUT) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !c.AllowAuthenticatedRequest() && // Todo check connection status @@ -706,7 +753,11 @@ func (c *COINUT) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e var currenciesToCheck []string if len(req.Pairs) == 0 { for i := range req.Pairs { - currenciesToCheck = append(currenciesToCheck, c.FormatExchangeCurrency(req.Pairs[i], asset.Spot).String()) + fpair, err := c.FormatExchangeCurrency(req.Pairs[i], asset.Spot) + if err != nil { + return nil, err + } + currenciesToCheck = append(currenciesToCheck, fpair.String()) } } else { for k := range c.instrumentMap.Instruments { @@ -720,10 +771,20 @@ func (c *COINUT) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e return nil, err } for i := range openOrders.Orders { + p, err := currency.NewPairFromString(currenciesToCheck[x]) + if err != nil { + return nil, err + } + + fpair, err := c.FormatExchangeCurrency(p, asset.Spot) + if err != nil { + return nil, err + } + orders = append(orders, order.Detail{ Exchange: c.Name, ID: strconv.FormatInt(openOrders.Orders[i].OrderID, 10), - Pair: c.FormatExchangeCurrency(currency.NewPairFromString(currenciesToCheck[x]), asset.Spot), + Pair: fpair, Side: order.Side(openOrders.Orders[i].Side), Date: time.Unix(0, openOrders.Orders[i].Timestamp), Status: order.Active, @@ -737,15 +798,28 @@ func (c *COINUT) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e } else { var instrumentsToUse []int64 for x := range req.Pairs { - curr := c.FormatExchangeCurrency(req.Pairs[x], - asset.Spot).String() + curr, err := c.FormatExchangeCurrency(req.Pairs[x], + asset.Spot) + if err != nil { + return nil, err + } instrumentsToUse = append(instrumentsToUse, - c.instrumentMap.LookupID(curr)) + c.instrumentMap.LookupID(curr.String())) } if len(instrumentsToUse) == 0 { instrumentsToUse = c.instrumentMap.GetInstrumentIDs() } + pairs, err := c.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + + format, err := c.GetPairFormat(asset.Spot, true) + if err != nil { + return nil, err + } + for x := range instrumentsToUse { openOrders, err := c.GetOpenOrders(instrumentsToUse[x]) if err != nil { @@ -753,9 +827,13 @@ func (c *COINUT) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e } for y := range openOrders.Orders { curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x]) - p := currency.NewPairFromFormattedPairs(curr, - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true)) + p, err := currency.NewPairFromFormattedPairs(curr, + pairs, + format) + if err != nil { + return nil, err + } + orderSide := order.Side(strings.ToUpper(openOrders.Orders[y].Side)) orderDate := time.Unix(openOrders.Orders[y].Timestamp, 0) orders = append(orders, order.Detail{ @@ -793,10 +871,15 @@ func (c *COINUT) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e } for x := range trades.Trades { curr := c.instrumentMap.LookupInstrument(trades.Trades[x].InstrumentID) + p, err := currency.NewPairFromString(curr) + if err != nil { + return nil, err + } + allOrders = append(allOrders, order.Detail{ Exchange: c.Name, ID: strconv.FormatInt(trades.Trades[x].OrderID, 10), - Pair: currency.NewPairFromString(curr), + Pair: p, Side: order.Side(trades.Trades[x].Side), Date: time.Unix(0, trades.Trades[x].Timestamp), Status: order.Filled, @@ -814,9 +897,13 @@ func (c *COINUT) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e } else { var instrumentsToUse []int64 for x := range req.Pairs { - curr := c.FormatExchangeCurrency(req.Pairs[x], - asset.Spot).String() - instrumentID := c.instrumentMap.LookupID(curr) + curr, err := c.FormatExchangeCurrency(req.Pairs[x], + asset.Spot) + if err != nil { + return nil, err + } + + instrumentID := c.instrumentMap.LookupID(curr.String()) if instrumentID > 0 { instrumentsToUse = append(instrumentsToUse, instrumentID) } @@ -824,6 +911,17 @@ func (c *COINUT) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e if len(instrumentsToUse) == 0 { instrumentsToUse = c.instrumentMap.GetInstrumentIDs() } + + pairs, err := c.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + + format, err := c.GetPairFormat(asset.Spot, true) + if err != nil { + return nil, err + } + for x := range instrumentsToUse { orders, err := c.GetTradeHistory(instrumentsToUse[x], -1, -1) if err != nil { @@ -831,9 +929,13 @@ func (c *COINUT) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e } for y := range orders.Trades { curr := c.instrumentMap.LookupInstrument(instrumentsToUse[x]) - p := currency.NewPairFromFormattedPairs(curr, - c.GetEnabledPairs(asset.Spot), - c.GetPairFormat(asset.Spot, true)) + p, err := currency.NewPairFromFormattedPairs(curr, + pairs, + format) + if err != nil { + return nil, err + } + orderSide := order.Side(strings.ToUpper(orders.Trades[y].Order.Side)) orderDate := time.Unix(orders.Trades[y].Order.Timestamp, 0) allOrders = append(allOrders, order.Detail{ @@ -854,25 +956,6 @@ func (c *COINUT) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e return allOrders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (c *COINUT) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - c.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (c *COINUT) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - c.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (c *COINUT) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return c.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (c *COINUT) AuthenticateWebsocket() error { return c.wsAuthenticate() diff --git a/exchanges/exchange.go b/exchanges/exchange.go index e22de1b2..b1f1cef7 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" @@ -17,6 +18,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/banking" ) @@ -197,19 +199,9 @@ func (e *Base) GetLastPairsUpdateTime() int64 { return e.CurrencyPairs.LastUpdated } -// SetAssetTypes checks the exchange asset types (whether it supports SPOT, -// Binary or Futures) and sets it to a default setting if it doesn't exist -func (e *Base) SetAssetTypes() { - if e.Config.CurrencyPairs.AssetTypes.JoinToString(",") == "" { - e.Config.CurrencyPairs.AssetTypes = e.CurrencyPairs.AssetTypes - } else if e.Config.CurrencyPairs.AssetTypes.JoinToString(",") != e.CurrencyPairs.AssetTypes.JoinToString(",") { - e.Config.CurrencyPairs.AssetTypes = e.CurrencyPairs.AssetTypes - } -} - // GetAssetTypes returns the available asset types for an individual exchange func (e *Base) GetAssetTypes() asset.Items { - return e.CurrencyPairs.AssetTypes + return e.CurrencyPairs.GetAssetTypes() } // GetPairAssetType returns the associated asset type for the currency pair @@ -218,7 +210,11 @@ func (e *Base) GetAssetTypes() asset.Items { func (e *Base) GetPairAssetType(c currency.Pair) (asset.Item, error) { assetTypes := e.GetAssetTypes() for i := range assetTypes { - if e.GetAvailablePairs(assetTypes[i]).Contains(c, true) { + avail, err := e.GetAvailablePairs(assetTypes[i]) + if err != nil { + return "", err + } + if avail.Contains(c, true) { return assetTypes[i], nil } } @@ -262,35 +258,53 @@ func (e *Base) SetCurrencyPairFormat() { assetTypes := e.GetAssetTypes() for x := range assetTypes { - if e.Config.CurrencyPairs.Get(assetTypes[x]) == nil { - r := e.CurrencyPairs.Get(assetTypes[x]) - if r == nil { + if _, err := e.Config.CurrencyPairs.Get(assetTypes[x]); err != nil { + ps, err := e.CurrencyPairs.Get(assetTypes[x]) + if err != nil { continue } - e.Config.CurrencyPairs.Store(assetTypes[x], *e.CurrencyPairs.Get(assetTypes[x])) + e.Config.CurrencyPairs.Store(assetTypes[x], *ps) } } } // SetConfigPairs sets the exchanges currency pairs to the pairs set in the config -func (e *Base) SetConfigPairs() { - assetTypes := e.GetAssetTypes() +func (e *Base) SetConfigPairs() error { + assetTypes := e.Config.CurrencyPairs.GetAssetTypes() + exchangeAssets := e.CurrencyPairs.GetAssetTypes() for x := range assetTypes { - cfgPS := e.Config.CurrencyPairs.Get(assetTypes[x]) - if cfgPS == nil { - continue + if !exchangeAssets.Contains(assetTypes[x]) { + log.Warnf(log.ExchangeSys, + "%s exchange asset type %s unsupported, please manually remove from configuration", + e.Name, + assetTypes[x]) } + cfgPS, err := e.Config.CurrencyPairs.Get(assetTypes[x]) + if err != nil { + return err + } + + var enabledAsset bool + if e.Config.CurrencyPairs.IsAssetEnabled(assetTypes[x]) == nil { + enabledAsset = true + } + e.CurrencyPairs.SetAssetEnabled(assetTypes[x], enabledAsset) + if e.Config.CurrencyPairs.UseGlobalFormat { e.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Available, false) e.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Enabled, true) continue } - exchPS := e.CurrencyPairs.Get(assetTypes[x]) + exchPS, err := e.CurrencyPairs.Get(assetTypes[x]) + if err != nil { + return err + } cfgPS.ConfigFormat = exchPS.ConfigFormat cfgPS.RequestFormat = exchPS.RequestFormat e.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Available, false) e.CurrencyPairs.StorePairs(assetTypes[x], cfgPS.Enabled, true) } + return nil } // GetAuthenticatedAPISupport returns whether the exchange supports @@ -322,26 +336,63 @@ func (e *Base) GetSupportedFeatures() FeaturesSupported { // GetPairFormat returns the pair format based on the exchange and // asset type -func (e *Base) GetPairFormat(assetType asset.Item, requestFormat bool) currency.PairFormat { +func (e *Base) GetPairFormat(assetType asset.Item, requestFormat bool) (currency.PairFormat, error) { if e.CurrencyPairs.UseGlobalFormat { if requestFormat { - return *e.CurrencyPairs.RequestFormat + if e.CurrencyPairs.RequestFormat == nil { + return currency.PairFormat{}, + errors.New("global request format is nil") + } + return *e.CurrencyPairs.RequestFormat, nil } - return *e.CurrencyPairs.ConfigFormat + + if e.CurrencyPairs.ConfigFormat == nil { + return currency.PairFormat{}, + errors.New("global config format is nil") + } + return *e.CurrencyPairs.ConfigFormat, nil + } + + ps, err := e.CurrencyPairs.Get(assetType) + if err != nil { + return currency.PairFormat{}, err } if requestFormat { - return *e.CurrencyPairs.Get(assetType).RequestFormat + if ps.RequestFormat == nil { + return currency.PairFormat{}, + errors.New("asset type request format is nil") + } + return *ps.RequestFormat, nil } - return *e.CurrencyPairs.Get(assetType).ConfigFormat + + if ps.ConfigFormat == nil { + return currency.PairFormat{}, + errors.New("asset type config format is nil") + } + return *ps.ConfigFormat, nil } // GetEnabledPairs is a method that returns the enabled currency pairs of -// the exchange by asset type -func (e *Base) GetEnabledPairs(assetType asset.Item) currency.Pairs { - format := e.GetPairFormat(assetType, false) - pairs := e.CurrencyPairs.GetPairs(assetType, true) - return pairs.Format(format.Delimiter, format.Index, format.Uppercase) +// the exchange by asset type, if the asset type is disabled this will return no +// enabled pairs +func (e *Base) GetEnabledPairs(a asset.Item) (currency.Pairs, error) { + err := e.CurrencyPairs.IsAssetEnabled(a) + if err != nil { + return nil, nil + } + format, err := e.GetPairFormat(a, false) + if err != nil { + return nil, err + } + enabledpairs, err := e.CurrencyPairs.GetPairs(a, true) + if err != nil { + return nil, err + } + return enabledpairs.Format(format.Delimiter, + format.Index, + format.Uppercase), + nil } // GetRequestFormattedPairAndAssetType is a method that returns the enabled currency pair of @@ -350,8 +401,16 @@ func (e *Base) GetRequestFormattedPairAndAssetType(p string) (currency.Pair, ass assetTypes := e.GetAssetTypes() var response currency.Pair for i := range assetTypes { - format := e.GetPairFormat(assetTypes[i], true) - pairs := e.CurrencyPairs.GetPairs(assetTypes[i], true) + format, err := e.GetPairFormat(assetTypes[i], true) + if err != nil { + return response, assetTypes[i], err + } + + pairs, err := e.CurrencyPairs.GetPairs(assetTypes[i], true) + if err != nil { + return response, assetTypes[i], err + } + for j := range pairs { formattedPair := pairs[j].Format(format.Delimiter, format.Uppercase) if strings.EqualFold(formattedPair.String(), p) { @@ -364,29 +423,57 @@ func (e *Base) GetRequestFormattedPairAndAssetType(p string) (currency.Pair, ass // GetAvailablePairs is a method that returns the available currency pairs // of the exchange by asset type -func (e *Base) GetAvailablePairs(assetType asset.Item) currency.Pairs { - format := e.GetPairFormat(assetType, false) - pairs := e.CurrencyPairs.GetPairs(assetType, false) - return pairs.Format(format.Delimiter, format.Index, format.Uppercase) +func (e *Base) GetAvailablePairs(assetType asset.Item) (currency.Pairs, error) { + format, err := e.GetPairFormat(assetType, false) + if err != nil { + return nil, err + } + pairs, err := e.CurrencyPairs.GetPairs(assetType, false) + if err != nil { + return nil, err + } + return pairs.Format(format.Delimiter, format.Index, format.Uppercase), nil } // SupportsPair returns true or not whether a currency pair exists in the // exchange available currencies or not -func (e *Base) SupportsPair(p currency.Pair, enabledPairs bool, assetType asset.Item) bool { +func (e *Base) SupportsPair(p currency.Pair, enabledPairs bool, assetType asset.Item) error { if enabledPairs { - return e.GetEnabledPairs(assetType).Contains(p, false) + pairs, err := e.GetEnabledPairs(assetType) + if err != nil { + return err + } + if pairs.Contains(p, false) { + return nil + } + return errors.New("pair not supported") } - return e.GetAvailablePairs(assetType).Contains(p, false) + + avail, err := e.GetAvailablePairs(assetType) + if err != nil { + return err + } + if avail.Contains(p, false) { + return nil + } + return errors.New("pair not supported") } // FormatExchangeCurrencies returns a string containing // the exchanges formatted currency pairs func (e *Base) FormatExchangeCurrencies(pairs []currency.Pair, assetType asset.Item) (string, error) { var currencyItems strings.Builder - pairFmt := e.GetPairFormat(assetType, true) + pairFmt, err := e.GetPairFormat(assetType, true) + if err != nil { + return "", err + } for x := range pairs { - currencyItems.WriteString(e.FormatExchangeCurrency(pairs[x], assetType).String()) + format, err := e.FormatExchangeCurrency(pairs[x], assetType) + if err != nil { + return "", err + } + currencyItems.WriteString(format.String()) if x == len(pairs)-1 { continue } @@ -401,9 +488,12 @@ func (e *Base) FormatExchangeCurrencies(pairs []currency.Pair, assetType asset.I // FormatExchangeCurrency is a method that formats and returns a currency pair // based on the user currency display preferences -func (e *Base) FormatExchangeCurrency(p currency.Pair, assetType asset.Item) currency.Pair { - pairFmt := e.GetPairFormat(assetType, true) - return p.Format(pairFmt.Delimiter, pairFmt.Uppercase) +func (e *Base) FormatExchangeCurrency(p currency.Pair, assetType asset.Item) (currency.Pair, error) { + pairFmt, err := e.GetPairFormat(assetType, true) + if err != nil { + return currency.Pair{}, err + } + return p.Format(pairFmt.Delimiter, pairFmt.Uppercase), nil } // SetEnabled is a method that sets if the exchange is enabled @@ -464,18 +554,26 @@ func (e *Base) SetupDefaults(exch *config.ExchangeConfig) error { e.HTTPDebugging = exch.HTTPDebugging e.SetHTTPClientUserAgent(exch.HTTPUserAgent) - e.SetAssetTypes() e.SetCurrencyPairFormat() - e.SetConfigPairs() - e.SetFeatureDefaults() - e.SetAPIURL() - e.SetAPICredentialDefaults() - e.SetClientProxyAddress(exch.ProxyAddress) - e.BaseCurrencies = exch.BaseCurrencies - if e.Features.Supports.Websocket { - return e.Websocket.Initialise() + err := e.SetConfigPairs() + if err != nil { + return err } + + e.SetFeatureDefaults() + err = e.SetAPIURL() + if err != nil { + return err + } + + e.SetAPICredentialDefaults() + + err = e.SetClientProxyAddress(exch.ProxyAddress) + if err != nil { + return err + } + e.BaseCurrencies = exch.BaseCurrencies return nil } @@ -564,7 +662,11 @@ func (e *Base) SetPairs(pairs currency.Pairs, assetType asset.Item, enabled bool return fmt.Errorf("%s SetPairs error - pairs is empty", e.Name) } - pairFmt := e.GetPairFormat(assetType, false) + pairFmt, err := e.GetPairFormat(assetType, false) + if err != nil { + return err + } + var newPairs currency.Pairs for x := range pairs { newPairs = append(newPairs, pairs[x].Format(pairFmt.Delimiter, @@ -579,10 +681,6 @@ func (e *Base) SetPairs(pairs currency.Pairs, assetType asset.Item, enabled bool // UpdatePairs updates the exchange currency pairs for either enabledPairs or // availablePairs func (e *Base) UpdatePairs(exchangeProducts currency.Pairs, assetType asset.Item, enabled, force bool) error { - if len(exchangeProducts) == 0 { - return fmt.Errorf("%s UpdatePairs error - exchangeProducts is empty", e.Name) - } - exchangeProducts = exchangeProducts.Upper() var products currency.Pairs for x := range exchangeProducts { @@ -592,37 +690,71 @@ func (e *Base) UpdatePairs(exchangeProducts currency.Pairs, assetType asset.Item products = append(products, exchangeProducts[x]) } - var newPairs, removedPairs currency.Pairs var updateType string - targetPairs := e.CurrencyPairs.GetPairs(assetType, enabled) + targetPairs, err := e.CurrencyPairs.GetPairs(assetType, enabled) + if err != nil { + return err + } if enabled { - newPairs, removedPairs = targetPairs.FindDifferences(products) updateType = "enabled" } else { - newPairs, removedPairs = targetPairs.FindDifferences(products) updateType = "available" } + newPairs, removedPairs := targetPairs.FindDifferences(products) if force || len(newPairs) > 0 || len(removedPairs) > 0 { if force { log.Debugf(log.ExchangeSys, - "%s forced update of %s [%v] pairs.", e.Name, updateType, + "%s forced update of %s [%v] pairs.", + e.Name, + updateType, strings.ToUpper(assetType.String())) } else { if len(newPairs) > 0 { log.Debugf(log.ExchangeSys, - "%s Updating pairs [%v] - New: %s.\n", e.Name, - strings.ToUpper(assetType.String()), newPairs) + "%s Updating %s pairs [%v] - Added: %s.\n", + e.Name, + updateType, + strings.ToUpper(assetType.String()), + newPairs) } if len(removedPairs) > 0 { log.Debugf(log.ExchangeSys, - "%s Updating pairs [%v] - Removed: %s.\n", e.Name, - strings.ToUpper(assetType.String()), removedPairs) + "%s Updating %s pairs [%v] - Removed: %s.\n", + e.Name, + updateType, + strings.ToUpper(assetType.String()), + removedPairs) } } + e.Config.CurrencyPairs.StorePairs(assetType, products, enabled) e.CurrencyPairs.StorePairs(assetType, products, enabled) + + if !enabled { + // If available pairs are changed we will remove currency pair items + // that are still included in the enabled pairs list. + enabledPairs, err := e.CurrencyPairs.GetPairs(assetType, true) + if err == nil { + return nil + } + _, remove := enabledPairs.FindDifferences(products) + for i := range remove { + enabledPairs = enabledPairs.Remove(remove[i]) + } + + if len(remove) > 0 { + log.Debugf(log.ExchangeSys, + "%s Checked and updated enabled pairs [%v] - Removed: %s.\n", + e.Name, + strings.ToUpper(assetType.String()), + remove) + + e.Config.CurrencyPairs.StorePairs(assetType, enabledPairs, true) + e.CurrencyPairs.StorePairs(assetType, enabledPairs, true) + } + } } return nil } @@ -675,27 +807,12 @@ func (e *Base) GetAPIURLSecondaryDefault() string { return e.API.Endpoints.URLSecondaryDefault } -// SupportsWebsocket returns whether or not the exchange supports -// websocket -func (e *Base) SupportsWebsocket() bool { - return e.Features.Supports.Websocket -} - // SupportsREST returns whether or not the exchange supports // REST func (e *Base) SupportsREST() bool { return e.Features.Supports.REST } -// IsWebsocketEnabled returns whether or not the exchange has its -// websocket client enabled -func (e *Base) IsWebsocketEnabled() bool { - if e.Websocket != nil { - return e.Websocket.IsEnabled() - } - return false -} - // GetWithdrawPermissions passes through the exchange's withdraw permissions func (e *Base) GetWithdrawPermissions() uint32 { return e.Features.Supports.WithdrawPermissions @@ -767,7 +884,8 @@ func (e *Base) FormatWithdrawPermissions() string { // SupportsAsset whether or not the supplied asset is supported // by the exchange func (e *Base) SupportsAsset(a asset.Item) bool { - return e.CurrencyPairs.AssetTypes.Contains(a) + _, ok := e.CurrencyPairs.Pairs[a] + return ok } // PrintEnabledPairs prints the exchanges enabled asset pairs @@ -804,6 +922,135 @@ func (e *Base) EnableRateLimiter() error { return e.Requester.EnableRateLimiter() } +// StoreAssetPairFormat initialises and stores a defined asset format +func (e *Base) StoreAssetPairFormat(a asset.Item, f currency.PairStore) error { + if a.String() == "" { + return fmt.Errorf("%s cannot add to pairs manager, no asset provided", + e.Name) + } + + if f.RequestFormat == nil { + return fmt.Errorf("%s cannot add to pairs manager, request pair format not provided", + e.Name) + } + + if f.ConfigFormat == nil { + return fmt.Errorf("%s cannot add to pairs manager, config pair format not provided", + e.Name) + } + + if e.CurrencyPairs.Pairs == nil { + e.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + } + + e.CurrencyPairs.Pairs[a] = &f + return nil +} + +// SetGlobalPairsManager sets defined asset and pairs management system with +// with global formatting +func (e *Base) SetGlobalPairsManager(request, config *currency.PairFormat, assets ...asset.Item) error { + if request == nil { + return fmt.Errorf("%s cannot set pairs manager, request pair format not provided", + e.Name) + } + + if config == nil { + return fmt.Errorf("%s cannot set pairs manager, config pair format not provided", + e.Name) + } + + if len(assets) == 0 { + return fmt.Errorf("%s cannot set pairs manager, no assets provided", + e.Name) + } + + e.CurrencyPairs.UseGlobalFormat = true + e.CurrencyPairs.RequestFormat = request + e.CurrencyPairs.ConfigFormat = config + + if e.CurrencyPairs.Pairs != nil { + return fmt.Errorf("%s cannot set pairs manager, pairs already set", + e.Name) + } + + e.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + + for i := range assets { + if assets[i].String() == "" { + e.CurrencyPairs.Pairs = nil + return fmt.Errorf("%s cannot set pairs manager, asset is empty string", + e.Name) + } + e.CurrencyPairs.Pairs[assets[i]] = new(currency.PairStore) + } + + return nil +} + +// GetWebsocket returns a pointer to the exchange websocket +func (e *Base) GetWebsocket() (*stream.Websocket, error) { + if e.Websocket == nil { + return nil, common.ErrFunctionNotSupported + } + return e.Websocket, nil +} + +// SupportsWebsocket returns whether or not the exchange supports +// websocket +func (e *Base) SupportsWebsocket() bool { + return e.Features.Supports.Websocket +} + +// IsWebsocketEnabled returns whether or not the exchange has its +// websocket client enabled +func (e *Base) IsWebsocketEnabled() bool { + if e.Websocket == nil { + return false + } + return e.Websocket.IsEnabled() +} + +// FlushWebsocketChannels refreshes websocket channel subscriptions based on +// websocket features. Used in the event of a pair/asset or subscription change. +func (e *Base) FlushWebsocketChannels() error { + if e.Websocket == nil { + return nil + } + return e.Websocket.FlushChannels() +} + +// SubscribeToWebsocketChannels appends to ChannelsToSubscribe +// which lets websocket.manageSubscriptions handle subscribing +func (e *Base) SubscribeToWebsocketChannels(channels []stream.ChannelSubscription) error { + if e.Websocket == nil { + return common.ErrFunctionNotSupported + } + return e.Websocket.SubscribeToChannels(channels) +} + +// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe +// which lets websocket.manageSubscriptions handle unsubscribing +func (e *Base) UnsubscribeToWebsocketChannels(channels []stream.ChannelSubscription) error { + if e.Websocket == nil { + return common.ErrFunctionNotSupported + } + return e.Websocket.UnsubscribeChannels(channels) +} + +// GetSubscriptions returns a copied list of subscriptions +func (e *Base) GetSubscriptions() ([]stream.ChannelSubscription, error) { + if e.Websocket == nil { + return nil, common.ErrFunctionNotSupported + } + return e.Websocket.GetSubscriptions(), nil +} + +// AuthenticateWebsocket sends an authentication message to the websocket +func (e *Base) AuthenticateWebsocket() error { + return common.ErrFunctionNotSupported +} + // KlineIntervalEnabled returns if requested interval is enabled on exchange func (e *Base) KlineIntervalEnabled(in kline.Interval) bool { return e.Features.Enabled.Kline.Intervals[in.Word()] diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index e61a4d28..29b03388 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -2,17 +2,20 @@ package exchange import ( "net/http" + "os" "strings" "testing" "time" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/banking" ) @@ -21,6 +24,13 @@ const ( defaultTestCurrencyPair = "BTC-USD" ) +func TestMain(m *testing.M) { + c := log.GenDefaultSettings() + log.GlobalLogConfig = &c + log.SetupGlobalLogger() + os.Exit(m.Run()) +} + func TestSupportsRESTTickerBatchUpdates(t *testing.T) { t.Parallel() @@ -98,7 +108,7 @@ func TestSetClientProxyAddress(t *testing.T) { Name: "rawr", Requester: requester} - newBase.Websocket = wshandler.New() + newBase.Websocket = stream.New() err := newBase.SetClientProxyAddress(":invalid") if err == nil { t.Error("SetClientProxyAddress parsed invalid URL") @@ -108,18 +118,18 @@ func TestSetClientProxyAddress(t *testing.T) { t.Error("SetClientProxyAddress error", err) } - err = newBase.SetClientProxyAddress("www.valid.com") + err = newBase.SetClientProxyAddress("http://www.valid.com") if err != nil { t.Error("SetClientProxyAddress error", err) } // calling this again will cause the ws check to fail - err = newBase.SetClientProxyAddress("www.valid.com") + err = newBase.SetClientProxyAddress("http://www.valid.com") if err == nil { t.Error("trying to set the same proxy addr should thrown an err for ws") } - if newBase.Websocket.GetProxyAddress() != "www.valid.com" { + if newBase.Websocket.GetProxyAddress() != "http://www.valid.com" { t.Error("SetClientProxyAddress error", err) } } @@ -253,46 +263,15 @@ func TestGetLastPairsUpdateTime(t *testing.T) { } } -func TestSetAssetTypes(t *testing.T) { - t.Parallel() - - b := Base{ - Config: &config.ExchangeConfig{ - CurrencyPairs: ¤cy.PairsManager{}, - }, - CurrencyPairs: currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Binary, - asset.Futures, - }, - }, - } - b.SetAssetTypes() - if len(b.GetAssetTypes()) != 3 { - t.Error("incorrect assets len") - } - - b.CurrencyPairs.AssetTypes = append(b.CurrencyPairs.AssetTypes, - asset.PerpetualSwap) - b.Config.CurrencyPairs.AssetTypes = asset.Items{ - asset.Index, - } - b.SetAssetTypes() - if len(b.GetAssetTypes()) != 4 { - t.Error("incorrect assets len") - } -} - func TestGetAssetTypes(t *testing.T) { t.Parallel() testExchange := Base{ CurrencyPairs: currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Binary, - asset.Futures, + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: new(currency.PairStore), + asset.Binary: new(currency.PairStore), + asset.Futures: new(currency.PairStore), }, }, } @@ -347,14 +326,17 @@ func TestSetCurrencyPairFormat(t *testing.T) { b.CurrencyPairs.RequestFormat = pFmt b.CurrencyPairs.ConfigFormat = pFmt b.SetCurrencyPairFormat() - if b.GetPairFormat(asset.Spot, true).Delimiter != "#" { + spot, err := b.GetPairFormat(asset.Spot, true) + if err != nil { + t.Fatal(err) + } + + if spot.Delimiter != "#" { t.Error("incorrect pair format delimiter") } // Test individual asset type formatting logic b.CurrencyPairs.UseGlobalFormat = false - // This will generate a nil pair store - b.CurrencyPairs.AssetTypes = asset.Items{asset.Index} // Store non-nil pair stores b.CurrencyPairs.Store(asset.Spot, currency.PairStore{ ConfigFormat: ¤cy.PairFormat{ @@ -367,10 +349,18 @@ func TestSetCurrencyPairFormat(t *testing.T) { }, }) b.SetCurrencyPairFormat() - if b.GetPairFormat(asset.Spot, false).Delimiter != "~" { + spot, err = b.GetPairFormat(asset.Spot, false) + if err != nil { + t.Fatal(err) + } + if spot.Delimiter != "~" { t.Error("incorrect pair format delimiter") } - if b.GetPairFormat(asset.Futures, false).Delimiter != ":)" { + futures, err := b.GetPairFormat(asset.Futures, false) + if err != nil { + t.Fatal(err) + } + if futures.Delimiter != ":)" { t.Error("incorrect pair format delimiter") } } @@ -386,7 +376,6 @@ func TestLoadConfigPairs(t *testing.T) { b := Base{ CurrencyPairs: currency.PairsManager{ UseGlobalFormat: true, - AssetTypes: asset.Items{asset.Spot}, RequestFormat: ¤cy.PairFormat{ Delimiter: ">", Uppercase: false, @@ -408,7 +397,10 @@ func TestLoadConfigPairs(t *testing.T) { } // Test a nil PairsManager - b.SetConfigPairs() + err := b.SetConfigPairs() + if err != nil { + t.Fatal(err) + } // Now setup a proper PairsManager b.Config.CurrencyPairs = ¤cy.PairsManager{ @@ -421,33 +413,51 @@ func TestLoadConfigPairs(t *testing.T) { Delimiter: "!", Uppercase: true, }, - AssetTypes: asset.Items{asset.Spot}, Pairs: map[asset.Item]*currency.PairStore{ asset.Spot: { - Enabled: pairs, - Available: pairs, - RequestFormat: ¤cy.PairFormat{}, - ConfigFormat: ¤cy.PairFormat{}, + AssetEnabled: convert.BoolPtr(true), + Enabled: pairs, + Available: pairs, }, }, } // Test UseGlobalFormat setting of pairs b.SetCurrencyPairFormat() - b.SetConfigPairs() + err = b.SetConfigPairs() + if err != nil { + t.Fatal(err) + } // Test four things: // 1) Config pairs are set // 2) pair format is set for RequestFormat // 3) pair format is set for ConfigFormat // 4) Config global format delimiter is updated based off exchange.Base - pFmt := b.GetPairFormat(asset.Spot, false) - p := b.GetEnabledPairs(asset.Spot)[0].Format(pFmt.Delimiter, - pFmt.Uppercase).String() + pFmt, err := b.GetPairFormat(asset.Spot, false) + if err != nil { + t.Fatal(err) + } + pairs, err = b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } + + p := pairs[0].Format(pFmt.Delimiter, pFmt.Uppercase).String() if p != "BTC^USD" { t.Errorf("incorrect value, expected BTC^USD") } - p = b.FormatExchangeCurrency(b.GetAvailablePairs(asset.Spot)[0], - asset.Spot).String() + + avail, err := b.GetAvailablePairs(asset.Spot) + if err != nil { + t.Fatal(err) + } + + format, err := b.FormatExchangeCurrency(avail[0], asset.Spot) + if err != nil { + t.Fatal(err) + } + + p = format.String() if p != "btc>usd" { t.Error("incorrect value, expected btc>usd") } @@ -459,7 +469,10 @@ func TestLoadConfigPairs(t *testing.T) { } // Test !UseGlobalFormat setting of pairs - exchPS := b.CurrencyPairs.Get(asset.Spot) + exchPS, err := b.CurrencyPairs.Get(asset.Spot) + if err != nil { + t.Fatal(err) + } exchPS.RequestFormat.Delimiter = "~" exchPS.RequestFormat.Uppercase = false exchPS.ConfigFormat.Delimiter = "/" @@ -476,18 +489,36 @@ func TestLoadConfigPairs(t *testing.T) { // 2) pair format is set for RequestFormat // 3) pair format is set for ConfigFormat // 4) Config pair store formats are the same as the exchanges - pFmt = b.GetPairFormat(asset.Spot, false) - p = b.GetEnabledPairs(asset.Spot)[2].Format(pFmt.Delimiter, - pFmt.Uppercase).String() + pFmt, err = b.GetPairFormat(asset.Spot, false) + if err != nil { + t.Fatal(err) + } + pairs, err = b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } + p = pairs[2].Format(pFmt.Delimiter, pFmt.Uppercase).String() if p != "xrp/usd" { t.Error("incorrect value, expected xrp/usd") } - p = b.FormatExchangeCurrency(b.GetAvailablePairs(asset.Spot)[2], - asset.Spot).String() + + avail, err = b.GetAvailablePairs(asset.Spot) + if err != nil { + t.Fatal(err) + } + + format, err = b.FormatExchangeCurrency(avail[2], asset.Spot) + if err != nil { + t.Fatal(err) + } + p = format.String() if p != "xrp~usd" { t.Error("incorrect value, expected xrp~usd") } - ps := b.Config.CurrencyPairs.Get(asset.Spot) + ps, err := b.Config.CurrencyPairs.Get(asset.Spot) + if err != nil { + t.Fatal(err) + } if ps.RequestFormat.Delimiter != "~" || ps.RequestFormat.Uppercase || ps.ConfigFormat.Delimiter != "/" || @@ -570,11 +601,17 @@ func TestGetPairFormat(t *testing.T) { b.CurrencyPairs.RequestFormat = ¤cy.PairFormat{ Delimiter: "~", } - pFmt := b.GetPairFormat(asset.Spot, true) + pFmt, err := b.GetPairFormat(asset.Spot, true) + if err != nil { + t.Fatal(err) + } if pFmt.Delimiter != "~" && !pFmt.Uppercase { t.Error("incorrect pair format values") } - pFmt = b.GetPairFormat(asset.Spot, false) + pFmt, err = b.GetPairFormat(asset.Spot, false) + if err != nil { + t.Fatal(err) + } if pFmt.Delimiter != "" && pFmt.Uppercase { t.Error("incorrect pair format values") } @@ -588,11 +625,17 @@ func TestGetPairFormat(t *testing.T) { Uppercase: true, }, }) - pFmt = b.GetPairFormat(asset.Spot, false) + pFmt, err = b.GetPairFormat(asset.Spot, false) + if err != nil { + t.Fatal(err) + } if pFmt.Delimiter != "" && pFmt.Uppercase { t.Error("incorrect pair format values") } - pFmt = b.GetPairFormat(asset.Spot, true) + pFmt, err = b.GetPairFormat(asset.Spot, true) + if err != nil { + t.Fatal(err) + } if pFmt.Delimiter != "~" && !pFmt.Uppercase { t.Error("incorrect pair format values") } @@ -605,70 +648,116 @@ func TestGetEnabledPairs(t *testing.T) { Name: "TESTNAME", } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{defaultTestCurrencyPair}), true) + defaultPairs, err := currency.NewPairsFromStrings([]string{defaultTestCurrencyPair}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, true) + b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, false) format := currency.PairFormat{ Delimiter: "-", Index: "", Uppercase: true, } - assetType := asset.Spot + err = b.CurrencyPairs.SetAssetEnabled(asset.Spot, true) + if err != nil { + t.Fatal(err) + } + b.CurrencyPairs.UseGlobalFormat = true b.CurrencyPairs.RequestFormat = &format b.CurrencyPairs.ConfigFormat = &format - c := b.GetEnabledPairs(assetType) + c, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } + if c[0].String() != defaultTestCurrencyPair { t.Error("Exchange GetAvailablePairs() incorrect string") } format.Delimiter = "~" b.CurrencyPairs.RequestFormat = &format - c = b.GetEnabledPairs(assetType) + c, err = b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } if c[0].String() != "BTC~USD" { t.Error("Exchange GetAvailablePairs() incorrect string") } format.Delimiter = "" b.CurrencyPairs.ConfigFormat = &format - c = b.GetEnabledPairs(assetType) + c, err = b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } if c[0].String() != "BTCUSD" { t.Error("Exchange GetAvailablePairs() incorrect string") } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{"BTCDOGE"}), true) + btcdoge, err := currency.NewPairsFromStrings([]string{"BTCDOGE"}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, true) + b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, false) format.Index = currency.BTC.String() b.CurrencyPairs.ConfigFormat = &format - c = b.GetEnabledPairs(assetType) + c, err = b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } if c[0].Base != currency.BTC && c[0].Quote != currency.DOGE { t.Error("Exchange GetAvailablePairs() incorrect string") } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{"BTC_USD"}), true) + btcusdUnderscore, err := currency.NewPairsFromStrings([]string{"BTC_USD"}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, true) + b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, false) b.CurrencyPairs.RequestFormat.Delimiter = "" b.CurrencyPairs.ConfigFormat.Delimiter = "_" - c = b.GetEnabledPairs(assetType) + c, err = b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } if c[0].Base != currency.BTC && c[0].Quote != currency.USD { t.Error("Exchange GetAvailablePairs() incorrect string") } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{"BTCDOGE"}), true) + b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, true) + b.CurrencyPairs.StorePairs(asset.Spot, btcdoge, false) b.CurrencyPairs.RequestFormat.Delimiter = "" b.CurrencyPairs.ConfigFormat.Delimiter = "" b.CurrencyPairs.ConfigFormat.Index = currency.BTC.String() - c = b.GetEnabledPairs(assetType) + c, err = b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } if c[0].Base != currency.BTC && c[0].Quote != currency.DOGE { t.Error("Exchange GetAvailablePairs() incorrect string") } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{"BTCUSD"}), true) + btcusd, err := currency.NewPairsFromStrings([]string{"BTCUSD"}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, btcusd, true) + b.CurrencyPairs.StorePairs(asset.Spot, btcusd, false) b.CurrencyPairs.ConfigFormat.Index = "" - c = b.GetEnabledPairs(assetType) + c, err = b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } if c[0].Base != currency.BTC && c[0].Quote != currency.USD { t.Error("Exchange GetAvailablePairs() incorrect string") } @@ -681,8 +770,12 @@ func TestGetAvailablePairs(t *testing.T) { Name: "TESTNAME", } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{defaultTestCurrencyPair}), false) + defaultPairs, err := currency.NewPairsFromStrings([]string{defaultTestCurrencyPair}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, defaultPairs, false) format := currency.PairFormat{ Delimiter: "-", Index: "", @@ -694,57 +787,96 @@ func TestGetAvailablePairs(t *testing.T) { b.CurrencyPairs.RequestFormat = &format b.CurrencyPairs.ConfigFormat = &format - c := b.GetAvailablePairs(assetType) + c, err := b.GetAvailablePairs(assetType) + if err != nil { + t.Fatal(err) + } + if c[0].String() != defaultTestCurrencyPair { t.Error("Exchange GetAvailablePairs() incorrect string") } format.Delimiter = "~" b.CurrencyPairs.RequestFormat = &format - c = b.GetAvailablePairs(assetType) + c, err = b.GetAvailablePairs(assetType) + if err != nil { + t.Fatal(err) + } + if c[0].String() != "BTC~USD" { t.Error("Exchange GetAvailablePairs() incorrect string") } format.Delimiter = "" b.CurrencyPairs.ConfigFormat = &format - c = b.GetAvailablePairs(assetType) + c, err = b.GetAvailablePairs(assetType) + if err != nil { + t.Fatal(err) + } + if c[0].String() != "BTCUSD" { t.Error("Exchange GetAvailablePairs() incorrect string") } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{"BTCDOGE"}), false) + dogePairs, err := currency.NewPairsFromStrings([]string{"BTCDOGE"}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, dogePairs, false) format.Index = currency.BTC.String() b.CurrencyPairs.ConfigFormat = &format - c = b.GetAvailablePairs(assetType) + c, err = b.GetAvailablePairs(assetType) + if err != nil { + t.Fatal(err) + } + if c[0].Base != currency.BTC && c[0].Quote != currency.DOGE { t.Error("Exchange GetAvailablePairs() incorrect string") } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{"BTC_USD"}), false) + btcusdUnderscore, err := currency.NewPairsFromStrings([]string{"BTC_USD"}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, btcusdUnderscore, false) b.CurrencyPairs.RequestFormat.Delimiter = "" b.CurrencyPairs.ConfigFormat.Delimiter = "_" - c = b.GetAvailablePairs(assetType) + c, err = b.GetAvailablePairs(assetType) + if err != nil { + t.Fatal(err) + } + if c[0].Base != currency.BTC && c[0].Quote != currency.USD { t.Error("Exchange GetAvailablePairs() incorrect string") } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{"BTCDOGE"}), false) + b.CurrencyPairs.StorePairs(asset.Spot, dogePairs, false) b.CurrencyPairs.RequestFormat.Delimiter = "" b.CurrencyPairs.ConfigFormat.Delimiter = "_" b.CurrencyPairs.ConfigFormat.Index = currency.BTC.String() - c = b.GetAvailablePairs(assetType) + c, err = b.GetAvailablePairs(assetType) + if err != nil { + t.Fatal(err) + } + if c[0].Base != currency.BTC && c[0].Quote != currency.DOGE { t.Error("Exchange GetAvailablePairs() incorrect string") } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{"BTCUSD"}), false) + btcusd, err := currency.NewPairsFromStrings([]string{"BTCUSD"}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, btcusd, false) b.CurrencyPairs.ConfigFormat.Index = "" - c = b.GetAvailablePairs(assetType) + c, err = b.GetAvailablePairs(assetType) + if err != nil { + t.Fatal(err) + } + if c[0].Base != currency.BTC && c[0].Quote != currency.USD { t.Error("Exchange GetAvailablePairs() incorrect string") } @@ -755,13 +887,29 @@ func TestSupportsPair(t *testing.T) { b := Base{ Name: "TESTNAME", + CurrencyPairs: currency.PairsManager{ + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: { + AssetEnabled: convert.BoolPtr(true), + }, + }, + }, } - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{ - defaultTestCurrencyPair, "ETH-USD"}), false) - b.CurrencyPairs.StorePairs(asset.Spot, - currency.NewPairsFromStrings([]string{defaultTestCurrencyPair}), true) + pairs, err := currency.NewPairsFromStrings([]string{defaultTestCurrencyPair, + "ETH-USD"}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, pairs, false) + + defaultpairs, err := currency.NewPairsFromStrings([]string{defaultTestCurrencyPair}) + if err != nil { + t.Fatal(err) + } + + b.CurrencyPairs.StorePairs(asset.Spot, defaultpairs, true) format := ¤cy.PairFormat{ Delimiter: "-", @@ -773,15 +921,20 @@ func TestSupportsPair(t *testing.T) { b.CurrencyPairs.ConfigFormat = format assetType := asset.Spot - if !b.SupportsPair(currency.NewPair(currency.BTC, currency.USD), true, assetType) { + if b.SupportsPair(currency.NewPair(currency.BTC, currency.USD), true, assetType) != nil { t.Error("Exchange SupportsPair() incorrect value") } - if !b.SupportsPair(currency.NewPair(currency.ETH, currency.USD), false, assetType) { + if b.SupportsPair(currency.NewPair(currency.ETH, currency.USD), false, assetType) != nil { t.Error("Exchange SupportsPair() incorrect value") } - if b.SupportsPair(currency.NewPairFromStrings("ASD", "ASDF"), true, assetType) { + asdasdf, err := currency.NewPairFromStrings("ASD", "ASDF") + if err != nil { + t.Fatal(err) + } + + if b.SupportsPair(asdasdf, true, assetType) == nil { t.Error("Exchange SupportsPair() incorrect value") } } @@ -805,10 +958,17 @@ func TestFormatExchangeCurrencies(t *testing.T) { }, }, } - + p1, err := currency.NewPairDelimiter("BTC_USD", "_") + if err != nil { + t.Fatal(err) + } + p2, err := currency.NewPairDelimiter("LTC_BTC", "_") + if err != nil { + t.Fatal(err) + } var pairs = []currency.Pair{ - currency.NewPairDelimiter("BTC_USD", "_"), - currency.NewPairDelimiter("LTC_BTC", "_"), + p1, + p2, } actual, err := e.FormatExchangeCurrencies(pairs, asset.Spot) @@ -839,7 +999,10 @@ func TestFormatExchangeCurrency(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USD) expected := defaultTestCurrencyPair - actual := b.FormatExchangeCurrency(p, asset.Spot) + actual, err := b.FormatExchangeCurrency(p, asset.Spot) + if err != nil { + t.Fatal(err) + } if actual.String() != expected { t.Errorf("Exchange TestFormatExchangeCurrency %s != %s", @@ -919,24 +1082,23 @@ func TestSetupDefaults(t *testing.T) { AuthenticatedSupport: true, }, } - if err := b.SetupDefaults(&cfg); err != nil { - t.Error(err) - } + b.SetupDefaults(&cfg) if cfg.HTTPTimeout.String() != "15s" { t.Error("HTTP timeout should be set to 15s") } // Test custom HTTP timeout is set cfg.HTTPTimeout = time.Second * 30 - if err := b.SetupDefaults(&cfg); err != nil { - t.Error(err) - } + b.SetupDefaults(&cfg) if cfg.HTTPTimeout.String() != "30s" { t.Error("HTTP timeout should be set to 30s") } // Test asset types - p := currency.NewPairDelimiter(defaultTestCurrencyPair, "-") + p, err := currency.NewPairDelimiter(defaultTestCurrencyPair, "-") + if err != nil { + t.Fatal(err) + } b.CurrencyPairs.Store(asset.Spot, currency.PairStore{ Enabled: currency.Pairs{ @@ -944,23 +1106,35 @@ func TestSetupDefaults(t *testing.T) { }, }, ) - if err := b.SetupDefaults(&cfg); err != nil { - t.Error(err) + b.SetupDefaults(&cfg) + ps, err := cfg.CurrencyPairs.Get(asset.Spot) + if err != nil { + t.Fatal(err) } - ps := cfg.CurrencyPairs.Get(asset.Spot) if !ps.Enabled.Contains(p, true) { t.Error("default pair should be stored in the configs pair store") } // Test websocket support - b.Websocket = wshandler.New() + b.Websocket = stream.New() b.Features.Supports.Websocket = true - if err := b.SetupDefaults(&cfg); err != nil { - t.Error(err) - } - b.Websocket.Setup(&wshandler.WebsocketSetup{ - Enabled: true, + b.SetupDefaults(&cfg) + err = b.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: false, + WebsocketTimeout: time.Second * 30, + Features: &protocol.Features{}, + DefaultURL: "ws://something.com", + RunningURL: "ws://something.com", + ExchangeName: "test", + Connector: func() error { return nil }, }) + if err != nil { + t.Fatal(err) + } + err = b.Websocket.Enable() + if err != nil { + t.Fatal(err) + } if !b.IsWebsocketEnabled() { t.Error("websocket should be enabled") } @@ -1081,7 +1255,11 @@ func TestSetPairs(t *testing.T) { ConfigFormat: ¤cy.PairFormat{ Uppercase: true, }, - Pairs: map[asset.Item]*currency.PairStore{}, + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: { + AssetEnabled: convert.BoolPtr(true), + }, + }, }, }, } @@ -1098,7 +1276,22 @@ func TestSetPairs(t *testing.T) { t.Error(err) } - if p := b.GetEnabledPairs(asset.Spot); len(p) != 1 { + err = b.SetPairs(pairs, asset.Spot, false) + if err != nil { + t.Error(err) + } + + err = b.SetConfigPairs() + if err != nil { + t.Fatal(err) + } + + p, err := b.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } + + if len(p) != 1 { t.Error("pairs shouldn't be nil") } } @@ -1115,14 +1308,34 @@ func TestUpdatePairs(t *testing.T) { t.Fatal("TestUpdatePairs failed to load config") } - UAC := Base{Name: defaultTestExchange} + UAC := Base{ + Name: defaultTestExchange, + CurrencyPairs: currency.PairsManager{ + Pairs: map[asset.Item]*currency.PairStore{ + asset.Spot: { + AssetEnabled: convert.BoolPtr(true), + }, + }, + }, + } UAC.Config = exchCfg - exchangeProducts := currency.NewPairsFromStrings([]string{"ltc", "btc", "usd", "aud", ""}) + exchangeProducts, err := currency.NewPairsFromStrings([]string{"ltcusd", + "btcusd", + "usdbtc", + "audusd"}) + if err != nil { + t.Fatal(err) + } err = UAC.UpdatePairs(exchangeProducts, asset.Spot, true, false) if err != nil { t.Errorf("TestUpdatePairs error: %s", err) } + err = UAC.UpdatePairs(exchangeProducts, asset.Spot, false, false) + if err != nil { + t.Errorf("TestUpdatePairs error: %s", err) + } + // Test updating the same new products, diff should be 0 err = UAC.UpdatePairs(exchangeProducts, asset.Spot, true, false) if err != nil { @@ -1130,14 +1343,24 @@ func TestUpdatePairs(t *testing.T) { } // Test force updating to only one product - exchangeProducts = currency.NewPairsFromStrings([]string{"btc"}) + exchangeProducts, err = currency.NewPairsFromStrings([]string{"btcusd"}) + if err != nil { + t.Fatal(err) + } + err = UAC.UpdatePairs(exchangeProducts, asset.Spot, true, true) if err != nil { t.Errorf("TestUpdatePairs error: %s", err) } // Test updating exchange products - exchangeProducts = currency.NewPairsFromStrings([]string{"ltc", "btc", "usd", "aud"}) + exchangeProducts, err = currency.NewPairsFromStrings([]string{"ltcusd", + "btcusd", + "usdbtc", + "audbtc"}) + if err != nil { + t.Fatal(err) + } UAC.Name = defaultTestExchange err = UAC.UpdatePairs(exchangeProducts, asset.Spot, false, false) if err != nil { @@ -1151,28 +1374,30 @@ func TestUpdatePairs(t *testing.T) { } // Test force updating to only one product - exchangeProducts = currency.NewPairsFromStrings([]string{"btc"}) + exchangeProducts, err = currency.NewPairsFromStrings([]string{"btcusd"}) + if err != nil { + t.Fatal(err) + } err = UAC.UpdatePairs(exchangeProducts, asset.Spot, false, true) if err != nil { t.Errorf("Forced Exchange UpdatePairs() error: %s", err) } // Test update currency pairs with btc excluded - exchangeProducts = currency.NewPairsFromStrings([]string{"ltc", "eth"}) + exchangeProducts, err = currency.NewPairsFromStrings([]string{"ltcusd", "ethusd"}) + if err != nil { + t.Fatal(err) + } err = UAC.UpdatePairs(exchangeProducts, asset.Spot, false, false) if err != nil { t.Errorf("Forced Exchange UpdatePairs() error: %s", err) } - // Test that empty exchange products should return an error - exchangeProducts = nil - err = UAC.UpdatePairs(exchangeProducts, asset.Spot, false, false) - if err == nil { - t.Errorf("empty available pairs should return an error") - } - // Test empty pair - p := currency.NewPairDelimiter(defaultTestCurrencyPair, "-") + p, err := currency.NewPairDelimiter(defaultTestCurrencyPair, "-") + if err != nil { + t.Fatal(err) + } pairs := currency.Pairs{ currency.Pair{}, p, @@ -1181,11 +1406,20 @@ func TestUpdatePairs(t *testing.T) { if err != nil { t.Errorf("Forced Exchange UpdatePairs() error: %s", err) } + err = UAC.UpdatePairs(pairs, asset.Spot, false, true) + if err != nil { + t.Errorf("Forced Exchange UpdatePairs() error: %s", err) + } UAC.CurrencyPairs.UseGlobalFormat = true UAC.CurrencyPairs.ConfigFormat = ¤cy.PairFormat{ Delimiter: "-", } - if !UAC.GetEnabledPairs(asset.Spot).Contains(p, true) { + + uacPairs, err := UAC.GetEnabledPairs(asset.Spot) + if err != nil { + t.Fatal(err) + } + if !uacPairs.Contains(p, true) { t.Fatal("expected currency pair not found") } } @@ -1298,8 +1532,16 @@ func TestIsWebsocketEnabled(t *testing.T) { t.Error("exchange doesn't support websocket") } - b.Websocket = wshandler.New() - err := b.Websocket.Setup(&wshandler.WebsocketSetup{Enabled: true}) + b.Websocket = stream.New() + err := b.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: true, + WebsocketTimeout: time.Second * 30, + Features: &protocol.Features{}, + DefaultURL: "ws://something.com", + RunningURL: "ws://something.com", + ExchangeName: "test", + Connector: func() error { return nil }, + }) if err != nil { t.Error(err) } @@ -1380,8 +1622,8 @@ func TestFormatWithdrawPermissions(t *testing.T) { func TestSupportsAsset(t *testing.T) { t.Parallel() var b Base - b.CurrencyPairs.AssetTypes = asset.Items{ - asset.Spot, + b.CurrencyPairs.Pairs = map[asset.Item]*currency.PairStore{ + asset.Spot: {}, } if !b.SupportsAsset(asset.Spot) { t.Error("spot should be supported") @@ -1426,9 +1668,12 @@ func TestGetAssetType(t *testing.T) { if err == nil { t.Fatal("error cannot be nil") } - b.CurrencyPairs.AssetTypes = asset.Items{asset.Spot} b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + Enabled: currency.Pairs{ + currency.NewPair(currency.BTC, currency.USD), + }, Available: currency.Pairs{ currency.NewPair(currency.BTC, currency.USD), }, @@ -1460,11 +1705,14 @@ func TestGetFormattedPairAndAssetType(t *testing.T) { b.CurrencyPairs.ConfigFormat = pFmt b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), Enabled: currency.Pairs{ currency.NewPair(currency.BTC, currency.USD), }, + Available: currency.Pairs{ + currency.NewPair(currency.BTC, currency.USD), + }, } - b.CurrencyPairs.AssetTypes = asset.Items{asset.Spot} p, a, err := b.GetRequestFormattedPairAndAssetType("btc#usd") if err != nil { t.Error(err) @@ -1481,6 +1729,85 @@ func TestGetFormattedPairAndAssetType(t *testing.T) { } } +func TestStoreAssetPairFormat(t *testing.T) { + b := Base{ + Config: &config.ExchangeConfig{Name: "kitties"}, + } + + err := b.StoreAssetPairFormat(asset.Item(""), currency.PairStore{}) + if err == nil { + t.Error("error cannot be nil") + } + + err = b.StoreAssetPairFormat(asset.Spot, currency.PairStore{}) + if err == nil { + t.Error("error cannot be nil") + } + + err = b.StoreAssetPairFormat(asset.Spot, currency.PairStore{ + RequestFormat: ¤cy.PairFormat{Uppercase: true}}) + if err == nil { + t.Error("error cannot be nil") + } + + err = b.StoreAssetPairFormat(asset.Spot, currency.PairStore{ + RequestFormat: ¤cy.PairFormat{Uppercase: true}, + ConfigFormat: ¤cy.PairFormat{Uppercase: true}}) + if err != nil { + t.Error(err) + } + + err = b.StoreAssetPairFormat(asset.Futures, currency.PairStore{ + RequestFormat: ¤cy.PairFormat{Uppercase: true}, + ConfigFormat: ¤cy.PairFormat{Uppercase: true}}) + if err != nil { + t.Error(err) + } +} + +func TestSetGlobalPairsManager(t *testing.T) { + b := Base{ + Config: &config.ExchangeConfig{Name: "kitties"}, + } + + err := b.SetGlobalPairsManager(nil, nil, "") + if err == nil { + t.Error("error cannot be nil") + } + + err = b.SetGlobalPairsManager(¤cy.PairFormat{Uppercase: true}, nil, "") + if err == nil { + t.Error("error cannot be nil") + } + + err = b.SetGlobalPairsManager(¤cy.PairFormat{Uppercase: true}, + ¤cy.PairFormat{Uppercase: true}) + if err == nil { + t.Error("error cannot be nil") + } + + err = b.SetGlobalPairsManager(¤cy.PairFormat{Uppercase: true}, + ¤cy.PairFormat{Uppercase: true}, "") + if err == nil { + t.Error("error cannot be nil") + } + + err = b.SetGlobalPairsManager(¤cy.PairFormat{Uppercase: true}, + ¤cy.PairFormat{Uppercase: true}, asset.Spot, asset.Binary) + if err != nil { + t.Error(err) + } + + if !b.SupportsAsset(asset.Binary) || !b.SupportsAsset(asset.Spot) { + t.Fatal("global pairs manager not set correctly") + } + + err = b.SetGlobalPairsManager(¤cy.PairFormat{Uppercase: true}, + ¤cy.PairFormat{Uppercase: true}, asset.Spot, asset.Binary) + if err == nil { + t.Error("error cannot be nil") + } +} func Test_FormatExchangeKlineInterval(t *testing.T) { testCases := []struct { name string diff --git a/exchanges/exchange_types.go b/exchanges/exchange_types.go index 15f5d253..38c9cbe2 100644 --- a/exchanges/exchange_types.go +++ b/exchanges/exchange_types.go @@ -8,7 +8,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" ) // Endpoint authentication types @@ -212,7 +212,7 @@ type Base struct { WebsocketResponseCheckTimeout time.Duration WebsocketResponseMaxLimit time.Duration WebsocketOrderbookBufferLimit int64 - Websocket *wshandler.Websocket + Websocket *stream.Websocket *request.Requester Config *config.ExchangeConfig } diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index 16d9f474..23c4858b 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -20,7 +20,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,20 +55,18 @@ func (e *EXMO) SetDefaults() { e.API.CredentialsValidator.RequiresKey = true e.API.CredentialsValidator.RequiresSecret = true - e.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - Separator: ",", - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{ + Delimiter: currency.UnderscoreDelimiter, + Uppercase: true, + Separator: ",", + } + configFmt := ¤cy.PairFormat{ + Delimiter: currency.UnderscoreDelimiter, + Uppercase: true, + } + err := e.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } e.Features = exchange.Features{ @@ -120,7 +117,6 @@ func (e *EXMO) Setup(exch *config.ExchangeConfig) error { e.SetEnabled(false) return nil } - return e.SetupDefaults(exch) } @@ -172,37 +168,45 @@ func (e *EXMO) UpdateTradablePairs(forceUpdate bool) error { return err } - return e.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + return e.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (e *EXMO) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) result, err := e.GetTicker() if err != nil { - return tickerPrice, err + return nil, err } if _, ok := result[p.String()]; !ok { - return tickerPrice, err + return nil, err + } + pairs, err := e.GetEnabledPairs(assetType) + if err != nil { + return nil, err } - pairs := e.GetEnabledPairs(assetType) for i := range pairs { for j := range result { if !strings.EqualFold(pairs[i].String(), j) { continue } - tickerPrice = &ticker.Price{ - Pair: pairs[i], - Last: result[j].Last, - Ask: result[j].Sell, - High: result[j].High, - Bid: result[j].Buy, - Low: result[j].Low, - Volume: result[j].Volume, - } - err = ticker.ProcessTicker(e.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Pair: pairs[i], + Last: result[j].Last, + Ask: result[j].Sell, + High: result[j].High, + Bid: result[j].Buy, + Low: result[j].Low, + Volume: result[j].Volume, + ExchangeName: e.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } @@ -229,7 +233,11 @@ func (e *EXMO) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbook // UpdateOrderbook updates and returns the orderbook for a currency pair func (e *EXMO) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - enabledPairs := e.GetEnabledPairs(assetType) + enabledPairs, err := e.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + pairsCollated, err := e.FormatExchangeCurrencies(enabledPairs, assetType) if err != nil { return nil, err @@ -241,7 +249,12 @@ func (e *EXMO) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderboo } for i := range enabledPairs { - data, ok := result[e.FormatExchangeCurrency(enabledPairs[i], assetType).String()] + curr, err := e.FormatExchangeCurrency(enabledPairs[i], assetType) + if err != nil { + return nil, err + } + + data, ok := result[curr.String()] if !ok { continue } @@ -473,11 +486,6 @@ func (e *EXMO) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.Re return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (e *EXMO) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrFunctionNotSupported -} - // GetFeeByType returns an estimate of fee based on type of transaction func (e *EXMO) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !e.AllowAuthenticatedRequest() && // Todo check connection status @@ -496,7 +504,11 @@ func (e *EXMO) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, err var orders []order.Detail for i := range resp { - symbol := currency.NewPairDelimiter(resp[i].Pair, "_") + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(resp[i].Pair, "_") + if err != nil { + return nil, err + } orderDate := time.Unix(resp[i].Created, 0) orderSide := order.Side(strings.ToUpper(resp[i].Type)) orders = append(orders, order.Detail{ @@ -524,7 +536,12 @@ func (e *EXMO) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, err var allTrades []UserTrades for i := range req.Pairs { - resp, err := e.GetUserTrades(e.FormatExchangeCurrency(req.Pairs[i], asset.Spot).String(), "", "10000") + fpair, err := e.FormatExchangeCurrency(req.Pairs[i], asset.Spot) + if err != nil { + return nil, err + } + + resp, err := e.GetUserTrades(fpair.String(), "", "10000") if err != nil { return nil, err } @@ -535,7 +552,10 @@ func (e *EXMO) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, err var orders []order.Detail for i := range allTrades { - symbol := currency.NewPairDelimiter(allTrades[i].Pair, "_") + symbol, err := currency.NewPairDelimiter(allTrades[i].Pair, "_") + if err != nil { + return nil, err + } orderDate := time.Unix(allTrades[i].Date, 0) orderSide := order.Side(strings.ToUpper(allTrades[i].Type)) orders = append(orders, order.Detail{ @@ -554,28 +574,6 @@ func (e *EXMO) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, err return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (e *EXMO) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (e *EXMO) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (e *EXMO) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (e *EXMO) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (e *EXMO) ValidateCredentials() error { diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index a86d0f0f..3fc09af6 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -17,13 +17,11 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) // FTX is the overarching type across this package type FTX struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } const ( diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index d1753249..22ae4db4 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -49,7 +49,7 @@ func TestMain(m *testing.M) { exchCfg.API.AuthenticatedWebsocketSupport = true exchCfg.API.Credentials.Key = apiKey exchCfg.API.Credentials.Secret = apiSecret - + f.Websocket = sharedtestvalues.NewTestWebsocket() err = f.Setup(exchCfg) if err != nil { log.Fatal(err) @@ -855,10 +855,13 @@ func TestGetFundingHistory(t *testing.T) { func TestGetHistoricCandles(t *testing.T) { t.Parallel() - currencyPair := currency.NewPairFromString(spotPair) + currencyPair, err := currency.NewPairFromString(spotPair) + if err != nil { + t.Fatal(err) + } start := time.Date(2019, 11, 12, 0, 0, 0, 0, time.UTC) end := start.AddDate(0, 0, 5) - _, err := f.GetHistoricCandles(currencyPair, asset.Spot, start, end, kline.OneDay) + _, err = f.GetHistoricCandles(currencyPair, asset.Spot, start, end, kline.OneDay) if err != nil { t.Fatal(err) } @@ -866,10 +869,13 @@ func TestGetHistoricCandles(t *testing.T) { func TestGetHistoricCandlesExtended(t *testing.T) { t.Parallel() - currencyPair := currency.NewPairFromString(spotPair) + currencyPair, err := currency.NewPairFromString(spotPair) + if err != nil { + t.Fatal(err) + } start := time.Date(2019, 11, 12, 0, 0, 0, 0, time.UTC) end := start.AddDate(0, 0, 5) - _, err := f.GetHistoricCandlesExtended(currencyPair, asset.Spot, start, end, kline.OneMin) + _, err = f.GetHistoricCandlesExtended(currencyPair, asset.Spot, start, end, kline.OneMin) if err != nil { t.Fatal(err) } @@ -1093,7 +1099,10 @@ func TestAcceptOTCQuote(t *testing.T) { func TestGetExchangeHistory(t *testing.T) { t.Parallel() - p := currency.NewPairFromString("ADA-PERP") + p, err := currency.NewPairFromString("ADA-PERP") + if err != nil { + t.Fatal(err) + } a, err := f.GetPairAssetType(p) if err != nil { t.Error(err) diff --git a/exchanges/ftx/ftx_websocket.go b/exchanges/ftx/ftx_websocket.go index 34703676..97fd83e1 100644 --- a/exchanges/ftx/ftx_websocket.go +++ b/exchanges/ftx/ftx_websocket.go @@ -11,15 +11,16 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -43,31 +44,35 @@ var obSuccess = make(map[currency.Pair]bool) // WsConnect connects to a websocket feed func (f *FTX) WsConnect() error { if !f.Websocket.IsEnabled() || !f.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := f.WebsocketConn.Dial(&dialer, http.Header{}) + err := f.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } - f.WebsocketConn.SetupPingHandler(wshandler.WebsocketPingHandler{ + f.Websocket.Conn.SetupPingHandler(stream.PingHandler{ MessageType: websocket.PingMessage, Delay: ftxWebsocketTimer, }) if f.Verbose { log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", f.Name) } - f.GenerateDefaultSubscriptions() + go f.wsReadData() if f.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - err := f.WsAuth() + err = f.WsAuth() if err != nil { f.Websocket.DataHandler <- err f.Websocket.SetCanUseAuthenticatedEndpoints(false) } - f.GenerateAuthSubscriptions() } - return nil + + subs, err := f.GenerateDefaultSubscriptions() + if err != nil { + return err + } + return f.Websocket.SubscribeToChannels(subs) } // WsAuth sends an authentication message to receive auth data @@ -87,81 +92,138 @@ func (f *FTX) WsAuth() error { Time: intNonce, }, } - return f.WebsocketConn.SendJSONMessage(req) + return f.Websocket.Conn.SendJSONMessage(req) } // Subscribe sends a websocket message to receive data from the channel -func (f *FTX) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - var sub WsSub - switch channelToSubscribe.Channel { - case wsFills, wsOrders, wsMarkets: +func (f *FTX) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors +channels: + for i := range channelsToSubscribe { + var sub WsSub + sub.Channel = channelsToSubscribe[i].Channel sub.Operation = subscribe - sub.Channel = channelToSubscribe.Channel - default: - a, err := f.GetPairAssetType(channelToSubscribe.Currency) - if err != nil { - return err + + switch channelsToSubscribe[i].Channel { + case wsFills, wsOrders, wsMarkets: + default: + a, err := f.GetPairAssetType(channelsToSubscribe[i].Currency) + if err != nil { + errs = append(errs, err) + continue channels + } + + formattedPair, err := f.FormatExchangeCurrency(channelsToSubscribe[i].Currency, a) + if err != nil { + errs = append(errs, err) + continue channels + } + sub.Market = formattedPair.String() } - sub.Operation = subscribe - sub.Channel = channelToSubscribe.Channel - sub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String() + err := f.Websocket.Conn.SendJSONMessage(sub) + if err != nil { + errs = append(errs, err) + continue + } + f.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) } - return f.WebsocketConn.SendJSONMessage(sub) + if errs != nil { + return errs + } + return nil +} + +// Unsubscribe sends a websocket message to stop receiving data from the channel +func (f *FTX) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors +channels: + for i := range channelsToUnsubscribe { + var unSub WsSub + unSub.Operation = unsubscribe + unSub.Channel = channelsToUnsubscribe[i].Channel + switch channelsToUnsubscribe[i].Channel { + case wsFills, wsOrders, wsMarkets: + default: + a, err := f.GetPairAssetType(channelsToUnsubscribe[i].Currency) + if err != nil { + errs = append(errs, err) + continue channels + } + + formattedPair, err := f.FormatExchangeCurrency(channelsToUnsubscribe[i].Currency, a) + if err != nil { + errs = append(errs, err) + continue channels + } + unSub.Market = formattedPair.String() + } + err := f.Websocket.Conn.SendJSONMessage(unSub) + if err != nil { + errs = append(errs, err) + continue + } + f.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) + } + if errs != nil { + return errs + } + return nil } // GenerateDefaultSubscriptions generates default subscription -func (f *FTX) GenerateDefaultSubscriptions() { - var subscriptions []wshandler.WebsocketChannelSubscription - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ +func (f *FTX) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: wsMarkets, }) var channels = []string{wsTicker, wsTrades, wsOrderbook} - for a := range f.CurrencyPairs.AssetTypes { - pairs := f.GetEnabledPairs(f.CurrencyPairs.AssetTypes[a]) + assets := f.GetAssetTypes() + for a := range assets { + pairs, err := f.GetEnabledPairs(assets[a]) + if err != nil { + return nil, err + } for z := range pairs { - newPair := currency.NewPairWithDelimiter(pairs[z].Base.String(), pairs[z].Quote.String(), "-") + newPair := currency.NewPairWithDelimiter(pairs[z].Base.String(), + pairs[z].Quote.String(), + "-") for x := range channels { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: channels[x], - Currency: newPair, - }) + subscriptions = append(subscriptions, + stream.ChannelSubscription{ + Channel: channels[x], + Currency: newPair, + Asset: assets[a], + }) } } } - f.Websocket.SubscribeToChannels(subscriptions) -} - -// GenerateAuthSubscriptions generates default subscription -func (f *FTX) GenerateAuthSubscriptions() { - var subscriptions []wshandler.WebsocketChannelSubscription - var channels = []string{wsOrders, wsFills} - for x := range channels { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: channels[x], - }) + if f.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + var authchan = []string{wsOrders, wsFills} + for x := range authchan { + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: authchan[x], + }) + } } - f.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // wsReadData gets and passes on websocket messages for processing func (f *FTX) wsReadData() { f.Websocket.Wg.Add(1) - defer f.Websocket.Wg.Done() for { select { case <-f.Websocket.ShutdownC: return - default: - resp, err := f.WebsocketConn.ReadMessage() - if err != nil { - f.Websocket.ReadMessageErrors <- err + resp := f.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return } - f.Websocket.TrafficAlert <- struct{}{} - err = f.wsHandleData(resp.Raw) + + err := f.wsHandleData(resp.Raw) if err != nil { f.Websocket.DataHandler <- err } @@ -187,7 +249,10 @@ func (f *FTX) wsHandleData(respRaw []byte) error { var a asset.Item market, ok := result["market"] if ok { - p = currency.NewPairFromString(market.(string)) + p, err = currency.NewPairFromString(market.(string)) + if err != nil { + return err + } a, err = f.GetPairAssetType(p) if err != nil { return err @@ -220,7 +285,10 @@ func (f *FTX) wsHandleData(respRaw []byte) error { } err = f.WsProcessUpdateOB(&resultData.OBData, p, a) if err != nil { - f.wsResubToOB(p) + err2 := f.wsResubToOB(p) + if err2 != nil { + f.Websocket.DataHandler <- err2 + } return err } case wsTrades: @@ -238,7 +306,7 @@ func (f *FTX) wsHandleData(respRaw []byte) error { Err: err, } } - f.Websocket.DataHandler <- wshandler.TradeData{ + f.Websocket.DataHandler <- stream.TradeData{ Timestamp: resultData.TradeData[z].Time, CurrencyPair: p, AssetType: a, @@ -254,7 +322,11 @@ func (f *FTX) wsHandleData(respRaw []byte) error { if err != nil { return err } - pair := currency.NewPairFromString(resultData.OrderData.Market) + var pair currency.Pair + pair, err = currency.NewPairFromString(resultData.OrderData.Market) + if err != nil { + return err + } var assetType asset.Item assetType, err = f.GetPairAssetType(pair) if err != nil { @@ -301,7 +373,7 @@ func (f *FTX) wsHandleData(respRaw []byte) error { } f.Websocket.DataHandler <- resultData.FillsData default: - f.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: f.Name + wshandler.UnhandledMessage + string(respRaw)} + f.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: f.Name + stream.UnhandledMessage + string(respRaw)} } case wsPartial: switch result["channel"] { @@ -310,7 +382,10 @@ func (f *FTX) wsHandleData(respRaw []byte) error { var a asset.Item market, ok := result["market"] if ok { - p = currency.NewPairFromString(market.(string)) + p, err = currency.NewPairFromString(market.(string)) + if err != nil { + return err + } a, err = f.GetPairAssetType(p) if err != nil { return err @@ -323,7 +398,10 @@ func (f *FTX) wsHandleData(respRaw []byte) error { } err = f.WsProcessPartialOB(&resultData.OBData, p, a) if err != nil { - f.wsResubToOB(p) + err2 := f.wsResubToOB(p) + if err2 != nil { + f.Websocket.DataHandler <- err2 + } return err } // reset obchecksum failure blockage for pair @@ -337,27 +415,16 @@ func (f *FTX) wsHandleData(respRaw []byte) error { f.Websocket.DataHandler <- resultData.Data } case "error": - f.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: f.Name + wshandler.UnhandledMessage + string(respRaw)} + f.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: f.Name + stream.UnhandledMessage + string(respRaw), + } } return nil } -// Unsubscribe sends a websocket message to stop receiving data from the channel -func (f *FTX) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - var unSub WsSub - a, err := f.GetPairAssetType(channelToSubscribe.Currency) - if err != nil { - return err - } - unSub.Operation = unsubscribe - unSub.Channel = channelToSubscribe.Channel - unSub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String() - return f.WebsocketConn.SendJSONMessage(unSub) -} - // WsProcessUpdateOB processes an update on the orderbook func (f *FTX) WsProcessUpdateOB(data *WsOrderbookData, p currency.Pair, a asset.Item) error { - update := wsorderbook.WebsocketOrderbookUpdate{ + update := buffer.Update{ Asset: a, Pair: p, UpdateTime: timestampFromFloat64(data.Time), @@ -391,27 +458,25 @@ func (f *FTX) WsProcessUpdateOB(data *WsOrderbookData, p currency.Pair, a asset. p) return errors.New("checksum failed") } - f.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: f.Name, - Asset: a, - Pair: p, - } - return nil } -func (f *FTX) wsResubToOB(p currency.Pair) { +func (f *FTX) wsResubToOB(p currency.Pair) error { if ok := obSuccess[p]; ok { - return + return nil } obSuccess[p] = true - channelToResubscribe := wshandler.WebsocketChannelSubscription{ + channelToResubscribe := &stream.ChannelSubscription{ Channel: wsOrderbook, Currency: p, } - f.Websocket.ResubscribeToChannel(channelToResubscribe) + err := f.Websocket.ResubscribeToChannel(channelToResubscribe) + if err != nil { + return fmt.Errorf("%s resubscribe to orderbook failure %s", f.Name, err) + } + return nil } // WsProcessPartialOB creates an OB from websocket data @@ -445,17 +510,7 @@ func (f *FTX) WsProcessPartialOB(data *WsOrderbookData, p currency.Pair, a asset Pair: p, ExchangeName: f.Name, } - - if err := f.Websocket.Orderbook.LoadSnapshot(&newOrderBook); err != nil { - return err - } - - f.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: f.Name, - Asset: a, - Pair: p, - } - return nil + return f.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } // CalcPartialOBChecksum calculates checksum of partial OB data received from WS diff --git a/exchanges/ftx/ftx_wrapper.go b/exchanges/ftx/ftx_wrapper.go index 7dcdfed2..89591206 100644 --- a/exchanges/ftx/ftx_wrapper.go +++ b/exchanges/ftx/ftx_wrapper.go @@ -18,8 +18,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -53,12 +53,7 @@ func (f *FTX) SetDefaults() { f.Verbose = true f.API.CredentialsValidator.RequiresKey = true f.API.CredentialsValidator.RequiresSecret = true - f.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Futures, - }, - } + spot := currency.PairStore{ RequestFormat: ¤cy.PairFormat{ Uppercase: true, @@ -79,8 +74,17 @@ func (f *FTX) SetDefaults() { Delimiter: "-", }, } - f.CurrencyPairs.Store(asset.Spot, spot) - f.CurrencyPairs.Store(asset.Futures, futures) + + err := f.StoreAssetPairFormat(asset.Spot, spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + err = f.StoreAssetPairFormat(asset.Futures, futures) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + f.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ REST: true, @@ -139,7 +143,7 @@ func (f *FTX) SetDefaults() { f.API.Endpoints.URLDefault = ftxAPIURL f.API.Endpoints.URL = f.API.Endpoints.URLDefault - f.Websocket = wshandler.New() + f.Websocket = stream.New() f.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit f.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout f.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -157,41 +161,28 @@ func (f *FTX) Setup(exch *config.ExchangeConfig) error { return err } - err = f.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: ftxWSURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: f.WsConnect, - Subscriber: f.Subscribe, - UnSubscriber: f.Unsubscribe, - Features: &f.Features.Supports.WebsocketCapabilities, - }) + err = f.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: ftxWSURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: f.WsConnect, + Subscriber: f.Subscribe, + UnSubscriber: f.Unsubscribe, + GenerateSubscriptions: f.GenerateDefaultSubscriptions, + Features: &f.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + }) if err != nil { return err } - - f.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: f.Name, - URL: f.Websocket.GetWebsocketURL(), - ProxyURL: f.Websocket.GetProxyAddress(), - Verbose: f.Verbose, + return f.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - f.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - false, - false, - false, - exch.Name) - return nil + }) } // Start starts the FTX go routine @@ -256,13 +247,17 @@ func (f *FTX) FetchTradablePairs(a asset.Item) ([]string, error) { // UpdateTradablePairs updates the exchanges available pairs and stores // them in the exchanges config func (f *FTX) UpdateTradablePairs(forceUpdate bool) error { - for x := range f.CurrencyPairs.AssetTypes { - pairs, err := f.FetchTradablePairs(f.CurrencyPairs.AssetTypes[x]) + assets := f.GetAssetTypes() + for x := range assets { + pairs, err := f.FetchTradablePairs(assets[x]) if err != nil { return err } - err = f.UpdatePairs(currency.NewPairsFromStrings(pairs), - f.CurrencyPairs.AssetTypes[x], false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + err = f.UpdatePairs(p, assets[x], false, forceUpdate) if err != nil { return err } @@ -272,26 +267,41 @@ func (f *FTX) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (f *FTX) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - allPairs := f.GetEnabledPairs(assetType) + allPairs, err := f.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + if !allPairs.Contains(p, true) { allPairs = append(allPairs, p) } + markets, err := f.GetMarkets() if err != nil { return nil, err } for a := range allPairs { + formattedPair, err := f.FormatExchangeCurrency(allPairs[a], assetType) + if err != nil { + return nil, err + } + for x := range markets { - if markets[x].Name != f.FormatExchangeCurrency(allPairs[a], assetType).String() { + if markets[x].Name != formattedPair.String() { continue } var resp ticker.Price - resp.Pair = currency.NewPairFromString(markets[x].Name) + resp.Pair, err = currency.NewPairFromString(markets[x].Name) + if err != nil { + return nil, err + } resp.Last = markets[x].Last resp.Bid = markets[x].Bid resp.Ask = markets[x].Ask resp.LastUpdated = time.Now() - err = ticker.ProcessTicker(f.Name, &resp, assetType) + resp.AssetType = assetType + resp.ExchangeName = f.Name + err = ticker.ProcessTicker(&resp) if err != nil { return nil, err } @@ -321,7 +331,11 @@ func (f *FTX) FetchOrderbook(currency currency.Pair, assetType asset.Item) (*ord // UpdateOrderbook updates and returns the orderbook for a currency pair func (f *FTX) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { orderBook := new(orderbook.Base) - tempResp, err := f.GetOrderbook(f.FormatExchangeCurrency(p, assetType).String(), 0) + formattedPair, err := f.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + tempResp, err := f.GetOrderbook(formattedPair.String(), 0) if err != nil { return orderBook, err } @@ -424,9 +438,15 @@ func (f *FTX) GetFundingHistory() ([]exchange.FundHistory, error) { // GetExchangeHistory returns historic trade data within the timeframe provided. func (f *FTX) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) { - marketName := f.FormatExchangeCurrency(p, assetType).String() + marketName, err := f.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } var resp []exchange.TradeHistory - trades, err := f.GetTrades(marketName, time.Unix(timestampStart.Unix(), 0), time.Unix(timestampEnd.Unix(), 0), 100) + trades, err := f.GetTrades(marketName.String(), + time.Unix(timestampStart.Unix(), 0), + time.Unix(timestampEnd.Unix(), 0), + 100) if err != nil { return nil, err } @@ -453,7 +473,10 @@ func (f *FTX) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestam if len(trades) != 100 { break } - trades, err = f.GetTrades(marketName, time.Unix(timestampStart.Unix(), 0), time.Unix(trades[len(trades)-1].Time.Unix(), 0), 100) + trades, err = f.GetTrades(marketName.String(), + time.Unix(timestampStart.Unix(), 0), + time.Unix(trades[len(trades)-1].Time.Unix(), 0), + 100) if err != nil { return resp, err } @@ -475,7 +498,12 @@ func (f *FTX) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { s.Side = order.Bid } - tempResp, err := f.Order(f.FormatExchangeCurrency(s.Pair, s.AssetType).String(), + formattedPair, err := f.FormatExchangeCurrency(s.Pair, s.AssetType) + if err != nil { + return resp, err + } + + tempResp, err := f.Order(formattedPair.String(), s.Side.String(), s.Type.String(), "", @@ -533,11 +561,16 @@ func (f *FTX) CancelOrder(order *order.Cancel) error { // CancelAllOrders cancels all orders associated with a currency pair func (f *FTX) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) { var resp order.CancelAllResponse - tempMap := make(map[string]string) - orders, err := f.GetOpenOrders(f.FormatExchangeCurrency(orderCancellation.Pair, orderCancellation.AssetType).String()) + formattedPair, err := f.FormatExchangeCurrency(orderCancellation.Pair, orderCancellation.AssetType) if err != nil { return resp, err } + orders, err := f.GetOpenOrders(formattedPair.String()) + if err != nil { + return resp, err + } + + tempMap := make(map[string]string) for x := range orders { _, err := f.DeleteOrder(strconv.FormatInt(orders[x].ID, 10)) if err != nil { @@ -598,7 +631,10 @@ func (f *FTX) GetOrderInfo(orderID string) (order.Detail, error) { if err != nil { return resp, err } - p := currency.NewPairFromString(orderData.Market) + p, err := currency.NewPairFromString(orderData.Market) + if err != nil { + return resp, err + } assetType, err := f.GetPairAssetType(p) if err != nil { return resp, err @@ -669,7 +705,7 @@ func (f *FTX) WithdrawFiatFundsToInternationalBank(_ *withdraw.Request) (*withdr } // GetWebsocket returns a pointer to the exchange websocket -func (f *FTX) GetWebsocket() (*wshandler.Websocket, error) { +func (f *FTX) GetWebsocket() (*stream.Websocket, error) { return f.Websocket, nil } @@ -681,12 +717,24 @@ func (f *FTX) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order if err != nil { return resp, err } + + formattedPair, err := f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType) + if err != nil { + return nil, err + } + var tempResp order.Detail - orderData, err := f.GetOpenOrders(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String()) + orderData, err := f.GetOpenOrders(formattedPair.String()) if err != nil { return resp, err } for y := range orderData { + var p currency.Pair + p, err = currency.NewPairFromString(orderData[y].Market) + if err != nil { + return nil, err + } + tempResp.ID = strconv.FormatInt(orderData[y].ID, 10) tempResp.Amount = orderData[y].Size tempResp.AssetType = assetType @@ -694,7 +742,7 @@ func (f *FTX) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order tempResp.Date = orderData[y].CreatedAt tempResp.Exchange = f.Name tempResp.ExecutedAmount = orderData[y].Size - orderData[y].RemainingSize - tempResp.Pair = currency.NewPairFromString(orderData[y].Market) + tempResp.Pair = p tempResp.Price = orderData[y].Price tempResp.RemainingAmount = orderData[y].RemainingSize var orderVars OrderVars @@ -713,18 +761,25 @@ func (f *FTX) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order tempResp.Fee = orderVars.Fee resp = append(resp, tempResp) } - triggerOrderData, err := f.GetOpenTriggerOrders(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(), getOrdersRequest.Type.String()) + + triggerOrderData, err := f.GetOpenTriggerOrders(formattedPair.String(), + getOrdersRequest.Type.String()) if err != nil { return resp, err } for z := range triggerOrderData { + var p currency.Pair + p, err = currency.NewPairFromString(triggerOrderData[z].Market) + if err != nil { + return nil, err + } tempResp.ID = strconv.FormatInt(triggerOrderData[z].ID, 10) tempResp.Amount = triggerOrderData[z].Size tempResp.AssetType = assetType tempResp.Date = triggerOrderData[z].CreatedAt tempResp.Exchange = f.Name tempResp.ExecutedAmount = triggerOrderData[z].FilledSize - tempResp.Pair = currency.NewPairFromString(triggerOrderData[z].Market) + tempResp.Pair = p tempResp.Price = triggerOrderData[z].AvgFillPrice tempResp.RemainingAmount = triggerOrderData[z].Size - triggerOrderData[z].FilledSize tempResp.TriggerPrice = triggerOrderData[z].TriggerPrice @@ -757,12 +812,24 @@ func (f *FTX) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]order if err != nil { return resp, err } - orderData, err := f.FetchOrderHistory(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(), + + formattedPair, err := f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], + assetType) + if err != nil { + return nil, err + } + + orderData, err := f.FetchOrderHistory(formattedPair.String(), getOrdersRequest.StartTicks, getOrdersRequest.EndTicks, "") if err != nil { return resp, err } for y := range orderData { + var p currency.Pair + p, err = currency.NewPairFromString(orderData[y].Market) + if err != nil { + return nil, err + } tempResp.ID = strconv.FormatInt(orderData[y].ID, 10) tempResp.Amount = orderData[y].Size tempResp.AssetType = assetType @@ -770,7 +837,7 @@ func (f *FTX) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]order tempResp.Date = orderData[y].CreatedAt tempResp.Exchange = f.Name tempResp.ExecutedAmount = orderData[y].Size - orderData[y].RemainingSize - tempResp.Pair = currency.NewPairFromString(orderData[y].Market) + tempResp.Pair = p tempResp.Price = orderData[y].Price tempResp.RemainingAmount = orderData[y].RemainingSize var orderVars OrderVars @@ -789,19 +856,28 @@ func (f *FTX) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]order tempResp.Fee = orderVars.Fee resp = append(resp, tempResp) } - triggerOrderData, err := f.GetTriggerOrderHistory(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(), - getOrdersRequest.StartTicks, getOrdersRequest.EndTicks, strings.ToLower(getOrdersRequest.Side.String()), strings.ToLower(getOrdersRequest.Type.String()), "") + triggerOrderData, err := f.GetTriggerOrderHistory(formattedPair.String(), + getOrdersRequest.StartTicks, + getOrdersRequest.EndTicks, + strings.ToLower(getOrdersRequest.Side.String()), + strings.ToLower(getOrdersRequest.Type.String()), + "") if err != nil { return resp, err } for z := range triggerOrderData { + var p currency.Pair + p, err = currency.NewPairFromString(triggerOrderData[z].Market) + if err != nil { + return nil, err + } tempResp.ID = strconv.FormatInt(triggerOrderData[z].ID, 10) tempResp.Amount = triggerOrderData[z].Size tempResp.AssetType = assetType tempResp.Date = triggerOrderData[z].CreatedAt tempResp.Exchange = f.Name tempResp.ExecutedAmount = triggerOrderData[z].FilledSize - tempResp.Pair = currency.NewPairFromString(triggerOrderData[z].Market) + tempResp.Pair = p tempResp.Price = triggerOrderData[z].AvgFillPrice tempResp.RemainingAmount = triggerOrderData[z].Size - triggerOrderData[z].FilledSize tempResp.TriggerPrice = triggerOrderData[z].TriggerPrice @@ -831,21 +907,14 @@ func (f *FTX) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { // SubscribeToWebsocketChannels appends to ChannelsToSubscribe // which lets websocket.manageSubscriptions handle subscribing -func (f *FTX) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - f.Websocket.SubscribeToChannels(channels) - return nil +func (f *FTX) SubscribeToWebsocketChannels(channels []stream.ChannelSubscription) error { + return f.Websocket.SubscribeToChannels(channels) } // UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe // which lets websocket.manageSubscriptions handle unsubscribing -func (f *FTX) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - f.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (f *FTX) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return f.Websocket.GetSubscriptions(), nil +func (f *FTX) UnsubscribeToWebsocketChannels(channels []stream.ChannelSubscription) error { + return f.Websocket.UnsubscribeChannels(channels) } // AuthenticateWebsocket sends an authentication message to the websocket @@ -868,7 +937,12 @@ func (f *FTX) GetHistoricCandles(p currency.Pair, a asset.Item, start, end time. } } - ohlcData, err := f.GetHistoricalData(f.FormatExchangeCurrency(p, a).String(), + formattedPair, err := f.FormatExchangeCurrency(p, a) + if err != nil { + return kline.Item{}, err + } + + ohlcData, err := f.GetHistoricalData(formattedPair.String(), f.FormatExchangeKlineInterval(interval), strconv.FormatInt(int64(f.Features.Enabled.Kline.ResultLimit), 10), start, end) @@ -912,8 +986,14 @@ func (f *FTX) GetHistoricCandlesExtended(p currency.Pair, a asset.Item, start, e } dates := kline.CalcDateRanges(start, end, interval, f.Features.Enabled.Kline.ResultLimit) + + formattedPair, err := f.FormatExchangeCurrency(p, a) + if err != nil { + return kline.Item{}, err + } + for x := range dates { - ohlcData, err := f.GetHistoricalData(f.FormatExchangeCurrency(p, a).String(), + ohlcData, err := f.GetHistoricalData(formattedPair.String(), f.FormatExchangeKlineInterval(interval), strconv.FormatInt(int64(f.Features.Enabled.Kline.ResultLimit), 10), dates[x].Start, dates[x].End) diff --git a/exchanges/gateio/gateio.go b/exchanges/gateio/gateio.go index ba22b045..b0f19d2c 100644 --- a/exchanges/gateio/gateio.go +++ b/exchanges/gateio/gateio.go @@ -15,7 +15,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -44,7 +43,6 @@ const ( // Gateio is the overarching type across this package type Gateio struct { - WebsocketConn *wshandler.WebsocketConnection exchange.Base } diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index 394c3139..daa1c993 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -18,7 +18,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -48,7 +48,7 @@ func TestMain(m *testing.M) { gConf.API.AuthenticatedWebsocketSupport = true gConf.API.Credentials.Key = apiKey gConf.API.Credentials.Secret = apiSecret - + g.Websocket = sharedtestvalues.NewTestWebsocket() err = g.Setup(gConf) if err != nil { log.Fatal("GateIO setup error", err) @@ -76,14 +76,14 @@ func TestGetMarketInfo(t *testing.T) { func TestSpotNewOrder(t *testing.T) { t.Parallel() - if !areTestAPIKeysSet() && !canManipulateRealOrders { + if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip() } _, err := g.SpotNewOrder(SpotNewOrderRequestParams{ Symbol: "btc_usdt", - Amount: 1.1, - Price: 10.1, + Amount: -1, + Price: 100000, Type: order.Sell.Lower(), }) if err != nil { @@ -94,7 +94,7 @@ func TestSpotNewOrder(t *testing.T) { func TestCancelExistingOrder(t *testing.T) { t.Parallel() - if !areTestAPIKeysSet() && !canManipulateRealOrders { + if !areTestAPIKeysSet() || !canManipulateRealOrders { t.Skip() } @@ -485,29 +485,18 @@ func TestGetOrderInfo(t *testing.T) { // TestWsGetBalance dials websocket, sends balance request. func TestWsGetBalance(t *testing.T) { if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) - } - g.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: g.Name, - URL: gateioWebsocketEndpoint, - Verbose: g.Verbose, - RateLimit: gateioWebsocketRateLimit, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + t.Skip(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := g.WebsocketConn.Dial(&dialer, http.Header{}) + err := g.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } go g.wsReadData() - resp, err := g.wsServerSignIn() + err = g.wsServerSignIn() if err != nil { t.Fatal(err) } - if resp.Result.Status != "success" { - t.Fatal("Unsuccessful login") - } _, err = g.wsGetBalance([]string{"EOS", "BTC"}) if err != nil { t.Error(err) @@ -521,30 +510,19 @@ func TestWsGetBalance(t *testing.T) { // TestWsGetOrderInfo dials websocket, sends order info request. func TestWsGetOrderInfo(t *testing.T) { if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) - } - g.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: g.Name, - URL: gateioWebsocketEndpoint, - Verbose: g.Verbose, - RateLimit: gateioWebsocketRateLimit, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + t.Skip(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := g.WebsocketConn.Dial(&dialer, http.Header{}) + err := g.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } go g.wsReadData() - resp, err := g.wsServerSignIn() + err = g.wsServerSignIn() if err != nil { t.Fatal(err) } - if resp.Result.Status != "success" { - t.Fatal("Unsuccessful login") - } - _, err = g.wsGetOrderInfo("EOS_USDT", 0, 1000) + _, err = g.wsGetOrderInfo("EOS_USDT", 0, 100) if err != nil { t.Error(err) } @@ -554,47 +532,34 @@ func setupWSTestAuth(t *testing.T) { if wsSetupRan { return } - if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport { - t.Skip(wshandler.WebsocketNotEnabled) + if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { + t.Skip(stream.WebsocketNotEnabled) } - g.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: g.Name, - URL: gateioWebsocketEndpoint, - Verbose: g.Verbose, - RateLimit: gateioWebsocketRateLimit, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, - } - var dialer websocket.Dialer - err := g.WebsocketConn.Dial(&dialer, http.Header{}) - - g.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - g.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() + err := g.Websocket.Connect() if err != nil { t.Fatal(err) } - go g.wsReadData() wsSetupRan = true } -// TestWsUnsubscribe dials websocket, sends an unsubscribe request. -func TestWsUnsubscribe(t *testing.T) { - setupWSTestAuth(t) - err := g.Unsubscribe(wshandler.WebsocketChannelSubscription{ - Channel: "ticker.subscribe", - Currency: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_"), - }) - if err != nil { - t.Error(err) - } -} - // TestWsSubscribe dials websocket, sends a subscribe request. func TestWsSubscribe(t *testing.T) { setupWSTestAuth(t) - err := g.Subscribe(wshandler.WebsocketChannelSubscription{ - Channel: "ticker.subscribe", - Currency: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_"), + err := g.Subscribe([]stream.ChannelSubscription{ + { + Channel: "ticker.subscribe", + Currency: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_"), + }, + }) + if err != nil { + t.Error(err) + } + + err = g.Unsubscribe([]stream.ChannelSubscription{ + { + Channel: "ticker.subscribe", + Currency: currency.NewPairWithDelimiter(currency.BTC.String(), currency.USDT.String(), "_"), + }, }) if err != nil { t.Error(err) @@ -603,12 +568,12 @@ func TestWsSubscribe(t *testing.T) { func TestWsTicker(t *testing.T) { pressXToJSON := []byte(`{ - "method": "ticker.update", - "params": + "method": "ticker.update", + "params": [ - "BTC_USDT", + "BTC_USDT", { - "period": 86400, + "period": 86400, "open": "0", "close": "0", "high": "0", @@ -630,9 +595,9 @@ func TestWsTicker(t *testing.T) { func TestWsTrade(t *testing.T) { pressXToJSON := []byte(`{ "method": "trades.update", - "params": + "params": [ - "BTC_USDT", + "BTC_USDT", [ { "id": 7172173, @@ -654,23 +619,23 @@ func TestWsTrade(t *testing.T) { func TestWsDepth(t *testing.T) { pressXToJSON := []byte(`{ - "method": "depth.update", + "method": "depth.update", "params": [ - true, + true, { "asks": [ - [ + [ "8000.00", "9.6250" ] ], - "bids": [ - [ + "bids": [ + [ "8000.00", "9.6250" - ] + ] ] - }, + }, "BTC_USDT" ], "id": null @@ -736,7 +701,7 @@ func TestWsOrderUpdate(t *testing.T) { func TestWsBalanceUpdate(t *testing.T) { pressXToJSON := []byte(`{ - "method": "balance.update", + "method": "balance.update", "params": [{"EOS": {"available": "96.765323611874", "freeze": "11"}}], "id": 1234 }`) @@ -765,18 +730,24 @@ func TestParseTime(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTC_USDT") + currencyPair, err := currency.NewPairFromString("BTC_USDT") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 6) - _, err := g.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = g.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("BTC_USDT") + currencyPair, err := currency.NewPairFromString("BTC_USDT") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 6) - _, err := g.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = g.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } @@ -812,3 +783,26 @@ func Test_FormatExchangeKlineInterval(t *testing.T) { }) } } + +func TestGenerateDefaultSubscriptions(t *testing.T) { + err := g.CurrencyPairs.EnablePair(asset.Spot, currency.NewPair( + currency.LTC, + currency.USDT, + )) + if err != nil { + t.Fatal(err) + } + subs, err := g.GenerateDefaultSubscriptions() + if err != nil { + t.Fatal(err) + } + + payload, err := g.generatePayload(subs) + if err != nil { + t.Fatal(err) + } + + if len(payload) != 4 { + t.Fatal("unexpected payload length") + } +} diff --git a/exchanges/gateio/gateio_types.go b/exchanges/gateio/gateio_types.go index a8483a73..184923b4 100644 --- a/exchanges/gateio/gateio_types.go +++ b/exchanges/gateio/gateio_types.go @@ -5,6 +5,7 @@ import ( "time" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" ) // TimeInterval Interval represents interval enum. @@ -379,9 +380,10 @@ var WithdrawalFees = map[currency.Code]float64{ // WebsocketRequest defines the initial request in JSON type WebsocketRequest struct { - ID int64 `json:"id"` - Method string `json:"method"` - Params []interface{} `json:"params"` + ID int64 `json:"id"` + Method string `json:"method"` + Params []interface{} `json:"params"` + Channels []stream.ChannelSubscription `json:"-"` // used for tracking associated channel subs on batched requests } // WebsocketResponse defines a websocket response from gateio @@ -437,6 +439,7 @@ type WebsocketBalanceCurrency struct { // WebSocketOrderQueryResult data returned from a websocket ordre query holds slice of WebSocketOrderQueryRecords type WebSocketOrderQueryResult struct { + Error WebsocketError `json:"error"` Limit int `json:"limit"` Offset int `json:"offset"` Total int `json:"total"` @@ -462,7 +465,10 @@ type WebSocketOrderQueryRecords struct { // WebsocketAuthenticationResponse contains the result of a login request type WebsocketAuthenticationResponse struct { - Error string `json:"error,omitempty"` + Error struct { + Code int `json:"code"` + Message string `json:"message"` + } `json:"error"` Result struct { Status string `json:"status"` } `json:"result"` @@ -478,7 +484,7 @@ type wsGetBalanceRequest struct { // WsGetBalanceResponse stores WS GetBalance response type WsGetBalanceResponse struct { - Error interface{} `json:"error"` + Error WebsocketError `json:"error"` Result map[string]WsGetBalanceResponseData `json:"result"` ID int64 `json:"id"` } diff --git a/exchanges/gateio/gateio_websocket.go b/exchanges/gateio/gateio_websocket.go index ac2bd682..40a400c3 100644 --- a/exchanges/gateio/gateio_websocket.go +++ b/exchanges/gateio/gateio_websocket.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" @@ -17,10 +18,9 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" - "github.com/thrasher-corp/gocryptotrader/log" ) const ( @@ -31,77 +31,87 @@ const ( // WsConnect initiates a websocket connection func (g *Gateio) WsConnect() error { if !g.Websocket.IsEnabled() || !g.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := g.WebsocketConn.Dial(&dialer, http.Header{}) + err := g.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } go g.wsReadData() - _, err = g.wsServerSignIn() - if err != nil { - log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", g.Name, err) - g.Websocket.SetCanUseAuthenticatedEndpoints(false) + + if g.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + err = g.wsServerSignIn() + if err != nil { + g.Websocket.DataHandler <- err + g.Websocket.SetCanUseAuthenticatedEndpoints(false) + } else { + var authsubs []stream.ChannelSubscription + authsubs, err = g.GenerateAuthenticatedSubscriptions() + if err != nil { + g.Websocket.DataHandler <- err + g.Websocket.SetCanUseAuthenticatedEndpoints(false) + } else { + err = g.Websocket.SubscribeToChannels(authsubs) + if err != nil { + g.Websocket.DataHandler <- err + g.Websocket.SetCanUseAuthenticatedEndpoints(false) + } + } + } } - g.GenerateAuthenticatedSubscriptions() - g.GenerateDefaultSubscriptions() - return nil + + subs, err := g.GenerateDefaultSubscriptions() + if err != nil { + return err + } + return g.Websocket.SubscribeToChannels(subs) } -func (g *Gateio) wsServerSignIn() (*WebsocketAuthenticationResponse, error) { - if !g.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", g.Name) - } +func (g *Gateio) wsServerSignIn() error { nonce := int(time.Now().Unix() * 1000) sigTemp := g.GenerateSignature(strconv.Itoa(nonce)) signature := crypto.Base64Encode(sigTemp) signinWsRequest := WebsocketRequest{ - ID: g.WebsocketConn.GenerateMessageID(true), + ID: g.Websocket.Conn.GenerateMessageID(false), Method: "server.sign", Params: []interface{}{g.API.Credentials.Key, signature, nonce}, } - resp, err := g.WebsocketConn.SendMessageReturnResponse(signinWsRequest.ID, signinWsRequest) + resp, err := g.Websocket.Conn.SendMessageReturnResponse(signinWsRequest.ID, + signinWsRequest) if err != nil { g.Websocket.SetCanUseAuthenticatedEndpoints(false) - return nil, err + return err } var response WebsocketAuthenticationResponse err = json.Unmarshal(resp, &response) if err != nil { g.Websocket.SetCanUseAuthenticatedEndpoints(false) - return nil, err + return err } if response.Result.Status == "success" { g.Websocket.SetCanUseAuthenticatedEndpoints(true) + return nil } - return &response, nil + + return fmt.Errorf("%s cannot authenticate websocket connection: %s", + g.Name, + response.Result.Status) } // wsReadData receives and passes on websocket messages for processing func (g *Gateio) wsReadData() { g.Websocket.Wg.Add(1) - - defer func() { - g.Websocket.Wg.Done() - }() + defer g.Websocket.Wg.Done() for { - select { - case <-g.Websocket.ShutdownC: + resp := g.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - - default: - resp, err := g.WebsocketConn.ReadMessage() - if err != nil { - g.Websocket.ReadMessageErrors <- err - return - } - g.Websocket.TrafficAlert <- struct{}{} - err = g.wsHandleData(resp.Raw) - if err != nil { - g.Websocket.DataHandler <- err - } + } + err := g.wsHandleData(resp.Raw) + if err != nil { + g.Websocket.DataHandler <- err } } } @@ -114,8 +124,7 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { } if result.ID > 0 { - if g.WebsocketConn.IsIDWaitingForResponse(result.ID) { - g.WebsocketConn.SetResponseIDAndData(result.ID, respRaw) + if g.Websocket.Match.IncomingWithData(result.ID, respRaw) { return nil } } @@ -125,8 +134,7 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { g.Websocket.SetCanUseAuthenticatedEndpoints(false) return fmt.Errorf("%v - authentication failed: %v", g.Name, err) } - return fmt.Errorf("%v error %s", - g.Name, result.Error.Message) + return fmt.Errorf("%v error %s", g.Name, result.Error.Message) } switch { @@ -142,6 +150,12 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { return err } + var p currency.Pair + p, err = currency.NewPairFromString(c) + if err != nil { + return err + } + g.Websocket.DataHandler <- &ticker.Price{ ExchangeName: g.Name, Open: wsTicker.Open, @@ -152,7 +166,7 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { Low: wsTicker.Low, Last: wsTicker.Last, AssetType: asset.Spot, - Pair: currency.NewPairFromString(c), + Pair: p, } case strings.Contains(result.Method, "trades"): @@ -167,6 +181,12 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { return err } + var p currency.Pair + p, err = currency.NewPairFromString(c) + if err != nil { + return err + } + for i := range trades { var tSide order.Side tSide, err = order.StringToOrderSide(trades[i].Type) @@ -176,9 +196,9 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { Err: err, } } - g.Websocket.DataHandler <- wshandler.TradeData{ + g.Websocket.DataHandler <- stream.TradeData{ Timestamp: time.Now(), - CurrencyPair: currency.NewPairFromString(c), + CurrencyPair: p, AssetType: asset.Spot, Exchange: g.Name, Price: trades[i].Price, @@ -244,7 +264,13 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { if err != nil { return err } - p := currency.NewPairFromString(invalidJSON["market"].(string)) + + var p currency.Pair + p, err = currency.NewPairFromString(invalidJSON["market"].(string)) + if err != nil { + return err + } + var a asset.Item a, err = g.GetPairAssetType(p) if err != nil { @@ -324,6 +350,12 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { g.Websocket.DataHandler <- errors.New("gatio websocket error - cannot access ask or bid data") } + var p currency.Pair + p, err = currency.NewPairFromString(c) + if err != nil { + return err + } + if IsSnapshot { if !askOk { g.Websocket.DataHandler <- errors.New("gatio websocket error - cannot access ask data") @@ -337,7 +369,7 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { newOrderBook.Asks = asks newOrderBook.Bids = bids newOrderBook.AssetType = asset.Spot - newOrderBook.Pair = currency.NewPairFromString(c) + newOrderBook.Pair = p newOrderBook.ExchangeName = g.Name err = g.Websocket.Orderbook.LoadSnapshot(&newOrderBook) @@ -345,25 +377,17 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { return err } } else { - err = g.Websocket.Orderbook.Update( - &wsorderbook.WebsocketOrderbookUpdate{ - Asks: asks, - Bids: bids, - Pair: currency.NewPairFromString(c), - UpdateTime: time.Now(), - Asset: asset.Spot, - }) + err = g.Websocket.Orderbook.Update(&buffer.Update{ + Asks: asks, + Bids: bids, + Pair: p, + UpdateTime: time.Now(), + Asset: asset.Spot, + }) if err != nil { return err } } - - g.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: currency.NewPairFromString(c), - Asset: asset.Spot, - Exchange: g.Name, - } - case strings.Contains(result.Method, "kline"): var data []interface{} err = json.Unmarshal(result.Params[0], &data) @@ -391,9 +415,14 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { return err } - g.Websocket.DataHandler <- wshandler.KlineData{ + p, err := currency.NewPairFromString(data[7].(string)) + if err != nil { + return err + } + + g.Websocket.DataHandler <- stream.KlineData{ Timestamp: time.Now(), - Pair: currency.NewPairFromString(data[7].(string)), + Pair: p, AssetType: asset.Spot, Exchange: g.Name, OpenPrice: open, @@ -403,36 +432,48 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { Volume: volume, } default: - g.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: g.Name + wshandler.UnhandledMessage + string(respRaw)} + g.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: g.Name + stream.UnhandledMessage + string(respRaw), + } return nil } return nil } -// GenerateAuthenticatedSubscriptions Adds authenticated subscriptions to websocket to be handled by ManageSubscriptions() -func (g *Gateio) GenerateAuthenticatedSubscriptions() { +// GenerateAuthenticatedSubscriptions returns authenticated subscriptions +func (g *Gateio) GenerateAuthenticatedSubscriptions() ([]stream.ChannelSubscription, error) { if !g.Websocket.CanUseAuthenticatedEndpoints() { - return + return nil, nil } var channels = []string{"balance.subscribe", "order.subscribe"} - var subscriptions []wshandler.WebsocketChannelSubscription - enabledCurrencies := g.GetEnabledPairs(asset.Spot) + var subscriptions []stream.ChannelSubscription + enabledCurrencies, err := g.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } for i := range channels { for j := range enabledCurrencies { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channels[i], Currency: enabledCurrencies[j], + Asset: asset.Spot, }) } } - g.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } -// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (g *Gateio) GenerateDefaultSubscriptions() { - var channels = []string{"ticker.subscribe", "trades.subscribe", "depth.subscribe", "kline.subscribe"} - var subscriptions []wshandler.WebsocketChannelSubscription - enabledCurrencies := g.GetEnabledPairs(asset.Spot) +// GenerateDefaultSubscriptions returns default subscriptions +func (g *Gateio) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var channels = []string{"ticker.subscribe", + "trades.subscribe", + "depth.subscribe", + "kline.subscribe"} + var subscriptions []stream.ChannelSubscription + enabledCurrencies, err := g.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } for i := range channels { for j := range enabledCurrencies { params := make(map[string]interface{}) @@ -442,66 +483,157 @@ func (g *Gateio) GenerateDefaultSubscriptions() { } else if strings.EqualFold(channels[i], "kline.subscribe") { params["interval"] = 1800 } - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + + fpair, err := g.FormatExchangeCurrency(enabledCurrencies[j], + asset.Spot) + if err != nil { + return nil, err + } + + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channels[i], - Currency: enabledCurrencies[j], + Currency: fpair.Upper(), Params: params, + Asset: asset.Spot, }) } } - g.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (g *Gateio) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - params := []interface{}{g.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).Upper()} - - for i := range channelToSubscribe.Params { - params = append(params, channelToSubscribe.Params[i]) - } - - subscribe := WebsocketRequest{ - ID: g.WebsocketConn.GenerateMessageID(true), - Method: channelToSubscribe.Channel, - Params: params, - } - - resp, err := g.WebsocketConn.SendMessageReturnResponse(subscribe.ID, subscribe) +func (g *Gateio) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + payloads, err := g.generatePayload(channelsToSubscribe) if err != nil { return err } - var response WebsocketAuthenticationResponse - err = json.Unmarshal(resp, &response) - if err != nil { - return err + + var errs common.Errors + for k := range payloads { + resp, err := g.Websocket.Conn.SendMessageReturnResponse(payloads[k].ID, payloads[k]) + if err != nil { + errs = append(errs, err) + continue + } + var response WebsocketAuthenticationResponse + err = json.Unmarshal(resp, &response) + if err != nil { + errs = append(errs, err) + continue + } + if response.Result.Status != "success" { + errs = append(errs, fmt.Errorf("%v could not subscribe to %v", + g.Name, + payloads[k].Method)) + continue + } + g.Websocket.AddSuccessfulSubscriptions(payloads[k].Channels...) } - if response.Result.Status != "success" { - return fmt.Errorf("%v could not subscribe to %v", g.Name, channelToSubscribe.Channel) + if errs != nil { + return errs } return nil } +func (g *Gateio) generatePayload(channelsToSubscribe []stream.ChannelSubscription) ([]WebsocketRequest, error) { + if len(channelsToSubscribe) == 0 { + return nil, errors.New("cannot generate payload, no channels supplied") + } + + var payloads []WebsocketRequest +channels: + for i := range channelsToSubscribe { + // Ensures params are in order + params := []interface{}{channelsToSubscribe[i].Currency} + if strings.EqualFold(channelsToSubscribe[i].Channel, "depth.subscribe") { + params = append(params, + channelsToSubscribe[i].Params["limit"], + channelsToSubscribe[i].Params["interval"]) + } else if strings.EqualFold(channelsToSubscribe[i].Channel, "kline.subscribe") { + params = append(params, channelsToSubscribe[i].Params["interval"]) + } + + for j := range payloads { + if payloads[j].Method == channelsToSubscribe[i].Channel { + switch { + case strings.EqualFold(channelsToSubscribe[i].Channel, "depth.subscribe"): + if len(payloads[j].Params) == 3 { + // If more than one currency pair we need to send as + // matrix + _, ok := payloads[j].Params[0].(currency.Pair) + if ok { + var bucket = payloads[j].Params + payloads[j].Params = nil + payloads[j].Params = append(payloads[j].Params, bucket) + } + } + + payloads[j].Params = append(payloads[j].Params, params) + case strings.EqualFold(channelsToSubscribe[i].Channel, "kline.subscribe"): + // Can only subscribe one market at the same time, market + // list is not supported currently. For multiple + // subscriptions, only the last one takes effect. + default: + payloads[j].Params = append(payloads[j].Params, params...) + } + payloads[j].Channels = append(payloads[j].Channels, channelsToSubscribe[i]) + continue channels + } + } + + payloads = append(payloads, WebsocketRequest{ + ID: g.Websocket.Conn.GenerateMessageID(false), + Method: channelsToSubscribe[i].Channel, + Params: params, + Channels: []stream.ChannelSubscription{channelsToSubscribe[i]}, + }) + } + return payloads, nil +} + // Unsubscribe sends a websocket message to stop receiving data from the channel -func (g *Gateio) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - unsbuscribeText := strings.Replace(channelToSubscribe.Channel, "subscribe", "unsubscribe", 1) - subscribe := WebsocketRequest{ - ID: g.WebsocketConn.GenerateMessageID(true), - Method: unsbuscribeText, - Params: []interface{}{g.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).Upper(), 1800}, - } - resp, err := g.WebsocketConn.SendMessageReturnResponse(subscribe.ID, subscribe) - if err != nil { - return err - } - var response WebsocketAuthenticationResponse - err = json.Unmarshal(resp, &response) - if err != nil { - return err - } - if response.Result.Status != "success" { - return fmt.Errorf("%v could not subscribe to %v", g.Name, channelToSubscribe.Channel) +func (g *Gateio) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + // NOTE: This function does not take in parameters, it cannot unsubscribe a + // single item but a full channel. i.e. if you subscribe to ticker BTC_USDT + // & LTC_USDT this function will unsubscribe both. This function will be + // kept unlinked to the websocket subsystem and a full connection flush will + // occur when currency items are disabled. + var channelsThusFar []string + for i := range channelsToUnsubscribe { + if common.StringDataCompare(channelsThusFar, + channelsToUnsubscribe[i].Channel) { + continue + } + + channelsThusFar = append(channelsThusFar, + channelsToUnsubscribe[i].Channel) + + unsubscribeText := strings.Replace(channelsToUnsubscribe[i].Channel, + "subscribe", + "unsubscribe", + 1) + + unsubscribe := WebsocketRequest{ + ID: g.Websocket.Conn.GenerateMessageID(false), + Method: unsubscribeText, + Params: []interface{}{channelsToUnsubscribe[i].Currency.String()}, + } + + resp, err := g.Websocket.Conn.SendMessageReturnResponse(unsubscribe.ID, + unsubscribe) + if err != nil { + return err + } + var response WebsocketAuthenticationResponse + err = json.Unmarshal(resp, &response) + if err != nil { + return err + } + if response.Result.Status != "success" { + return fmt.Errorf("%v could not subscribe to %v", + g.Name, + channelsToUnsubscribe[i].Channel) + } } return nil } @@ -511,11 +643,11 @@ func (g *Gateio) wsGetBalance(currencies []string) (*WsGetBalanceResponse, error return nil, fmt.Errorf("%v not authorised to get balance", g.Name) } balanceWsRequest := wsGetBalanceRequest{ - ID: g.WebsocketConn.GenerateMessageID(false), + ID: g.Websocket.Conn.GenerateMessageID(false), Method: "balance.query", Params: currencies, } - resp, err := g.WebsocketConn.SendMessageReturnResponse(balanceWsRequest.ID, balanceWsRequest) + resp, err := g.Websocket.Conn.SendMessageReturnResponse(balanceWsRequest.ID, balanceWsRequest) if err != nil { return nil, err } @@ -525,6 +657,12 @@ func (g *Gateio) wsGetBalance(currencies []string) (*WsGetBalanceResponse, error return &balance, err } + if balance.Error.Message != "" { + return nil, fmt.Errorf("%s websocket error: %s", + g.Name, + balance.Error.Message) + } + return &balance, nil } @@ -533,7 +671,7 @@ func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) (*WebSocketOrd return nil, fmt.Errorf("%v not authorised to get order info", g.Name) } ord := WebsocketRequest{ - ID: g.WebsocketConn.GenerateMessageID(true), + ID: g.Websocket.Conn.GenerateMessageID(false), Method: "order.query", Params: []interface{}{ market, @@ -541,14 +679,23 @@ func (g *Gateio) wsGetOrderInfo(market string, offset, limit int) (*WebSocketOrd limit, }, } - resp, err := g.WebsocketConn.SendMessageReturnResponse(ord.ID, ord) + + resp, err := g.Websocket.Conn.SendMessageReturnResponse(ord.ID, ord) if err != nil { return nil, err } + var orderQuery WebSocketOrderQueryResult err = json.Unmarshal(resp, &orderQuery) if err != nil { return &orderQuery, err } + + if orderQuery.Error.Message != "" { + return nil, fmt.Errorf("%s websocket error: %s", + g.Name, + orderQuery.Error.Message) + } + return &orderQuery, nil } diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index d240aa05..60e59104 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -20,8 +20,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -57,18 +57,11 @@ func (g *Gateio) SetDefaults() { g.API.CredentialsValidator.RequiresKey = true g.API.CredentialsValidator.RequiresSecret = true - g.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "_", - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Delimiter: currency.UnderscoreDelimiter} + configFmt := ¤cy.PairFormat{Delimiter: currency.UnderscoreDelimiter, Uppercase: true} + err := g.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } g.Features = exchange.Features{ @@ -99,8 +92,7 @@ func (g *Gateio) SetDefaults() { OrderbookFetching: true, TradeFetching: true, KlineFetching: true, - Subscribe: true, - Unsubscribe: true, + FullPayloadSubscribe: true, AuthenticatedEndpoints: true, MessageCorrelation: true, GetOrder: true, @@ -139,7 +131,7 @@ func (g *Gateio) SetDefaults() { g.API.Endpoints.URLSecondaryDefault = gateioMarketURL g.API.Endpoints.URLSecondary = g.API.Endpoints.URLSecondaryDefault g.API.Endpoints.WebsocketURL = gateioWebsocketEndpoint - g.Websocket = wshandler.New() + g.Websocket = stream.New() g.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit g.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout g.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -157,42 +149,30 @@ func (g *Gateio) Setup(exch *config.ExchangeConfig) error { return err } - err = g.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: gateioWebsocketEndpoint, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: g.WsConnect, - Subscriber: g.Subscribe, - UnSubscriber: g.Unsubscribe, - Features: &g.Features.Supports.WebsocketCapabilities, - }) + err = g.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: gateioWebsocketEndpoint, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: g.WsConnect, + Subscriber: g.Subscribe, + GenerateSubscriptions: g.GenerateDefaultSubscriptions, + Features: &g.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + BufferEnabled: true, + }) if err != nil { return err } - g.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: g.Name, - URL: g.Websocket.GetWebsocketURL(), - ProxyURL: g.Websocket.GetProxyAddress(), - Verbose: g.Verbose, + return g.Websocket.SetupNewConnection(stream.ConnectionSetup{ + RateLimit: gateioWebsocketRateLimit, ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - RateLimit: gateioWebsocketRateLimit, - } - - g.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - true, - false, - false, - false, - exch.Name) - return nil + }) } // Start starts the GateIO go routine @@ -233,35 +213,42 @@ func (g *Gateio) UpdateTradablePairs(forceUpdate bool) error { return err } - return g.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return g.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (g *Gateio) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) result, err := g.GetTickers() if err != nil { - return tickerPrice, err + return nil, err + } + pairs, err := g.GetEnabledPairs(assetType) + if err != nil { + return nil, err } - pairs := g.GetEnabledPairs(assetType) for i := range pairs { for k := range result { if !strings.EqualFold(k, pairs[i].String()) { continue } - tickerPrice = &ticker.Price{ - Last: result[k].Last, - High: result[k].High, - Low: result[k].Low, - Volume: result[k].BaseVolume, - QuoteVolume: result[k].QuoteVolume, - Open: result[k].Open, - Close: result[k].Close, - Pair: pairs[i], - } - err = ticker.ProcessTicker(g.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: result[k].Last, + High: result[k].High, + Low: result[k].Low, + Volume: result[k].BaseVolume, + QuoteVolume: result[k].QuoteVolume, + Open: result[k].Open, + Close: result[k].Close, + Pair: pairs[i], + ExchangeName: g.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } @@ -290,9 +277,12 @@ func (g *Gateio) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbo // UpdateOrderbook updates and returns the orderbook for a currency pair func (g *Gateio) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { orderBook := new(orderbook.Base) - curr := g.FormatExchangeCurrency(p, assetType).String() + curr, err := g.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } - orderbookNew, err := g.GetOrderbook(curr) + orderbookNew, err := g.GetOrderbook(curr.String()) if err != nil { return orderBook, err } @@ -479,8 +469,13 @@ func (g *Gateio) CancelOrder(order *order.Cancel) error { if err != nil { return err } - _, err = g.CancelExistingOrder(orderIDInt, - g.FormatExchangeCurrency(order.Pair, order.AssetType).String()) + + fpair, err := g.FormatExchangeCurrency(order.Pair, order.AssetType) + if err != nil { + return err + } + + _, err = g.CancelExistingOrder(orderIDInt, fpair.String()) return err } @@ -516,6 +511,12 @@ func (g *Gateio) GetOrderInfo(orderID string) (order.Detail, error) { if err != nil { return orderDetail, errors.New("failed to get open orders") } + + format, err := g.GetPairFormat(asset.Spot, false) + if err != nil { + return orderDetail, err + } + for x := range orders.Orders { if orders.Orders[x].OrderNumber != orderID { continue @@ -528,8 +529,11 @@ func (g *Gateio) GetOrderInfo(orderID string) (order.Detail, error) { orderDetail.Date = time.Unix(orders.Orders[x].Timestamp, 0) orderDetail.Status = order.Status(orders.Orders[x].Status) orderDetail.Price = orders.Orders[x].Rate - orderDetail.Pair = currency.NewPairDelimiter(orders.Orders[x].CurrencyPair, - g.GetPairFormat(asset.Spot, false).Delimiter) + orderDetail.Pair, err = currency.NewPairDelimiter(orders.Orders[x].CurrencyPair, + format.Delimiter) + if err != nil { + return orderDetail, err + } if strings.EqualFold(orders.Orders[x].Type, order.Ask.String()) { orderDetail.Side = order.Ask } else if strings.EqualFold(orders.Orders[x].Type, order.Bid.String()) { @@ -572,11 +576,6 @@ func (g *Gateio) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw. return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (g *Gateio) GetWebsocket() (*wshandler.Websocket, error) { - return g.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (g *Gateio) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !g.AllowAuthenticatedRequest() && // Todo check connection status @@ -609,11 +608,15 @@ func (g *Gateio) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e if resp.WebSocketOrderQueryRecords[j].OrderType == 1 { orderType = order.Limit } + p, err := currency.NewPairFromString(resp.WebSocketOrderQueryRecords[j].Market) + if err != nil { + return nil, err + } orders = append(orders, order.Detail{ Exchange: g.Name, AccountID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].User, 10), ID: strconv.FormatInt(resp.WebSocketOrderQueryRecords[j].ID, 10), - Pair: currency.NewPairFromString(resp.WebSocketOrderQueryRecords[j].Market), + Pair: p, Side: orderSide, Type: orderType, Date: convert.TimeFromUnixTimestampDecimal(resp.WebSocketOrderQueryRecords[j].Ctime), @@ -634,13 +637,21 @@ func (g *Gateio) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e return nil, err } + format, err := g.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + for i := range resp.Orders { if resp.Orders[i].Status != "open" { continue } - - symbol := currency.NewPairDelimiter(resp.Orders[i].CurrencyPair, - g.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(resp.Orders[i].CurrencyPair, + format.Delimiter) + if err != nil { + return nil, err + } side := order.Side(strings.ToUpper(resp.Orders[i].Type)) orderDate := time.Unix(resp.Orders[i].Timestamp, 0) orders = append(orders, order.Detail{ @@ -673,16 +684,24 @@ func (g *Gateio) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e trades = append(trades, resp.Trades...) } + format, err := g.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail - for _, trade := range trades { - symbol := currency.NewPairDelimiter(trade.Pair, - g.GetPairFormat(asset.Spot, false).Delimiter) - side := order.Side(strings.ToUpper(trade.Type)) - orderDate := time.Unix(trade.TimeUnix, 0) + for i := range trades { + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(trades[i].Pair, format.Delimiter) + if err != nil { + return nil, err + } + side := order.Side(strings.ToUpper(trades[i].Type)) + orderDate := time.Unix(trades[i].TimeUnix, 0) orders = append(orders, order.Detail{ - ID: strconv.FormatInt(trade.OrderID, 10), - Amount: trade.Amount, - Price: trade.Rate, + ID: strconv.FormatInt(trades[i].OrderID, 10), + Amount: trades[i].Amount, + Price: trades[i].Rate, Date: orderDate, Side: side, Exchange: g.Name, @@ -695,29 +714,9 @@ func (g *Gateio) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (g *Gateio) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - g.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (g *Gateio) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - g.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (g *Gateio) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return g.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (g *Gateio) AuthenticateWebsocket() error { - _, err := g.wsServerSignIn() - return err + return g.wsServerSignIn() } // ValidateCredentials validates current credentials used for wrapper @@ -741,8 +740,13 @@ func (g *Gateio) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end } hours := end.Sub(start).Hours() + formattedPair, err := g.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + params := KlinesRequestParams{ - Symbol: g.FormatExchangeCurrency(pair, a).String(), + Symbol: formattedPair.String(), GroupSec: g.FormatExchangeKlineInterval(interval), HourSize: int(hours), } diff --git a/exchanges/gemini/gemini.go b/exchanges/gemini/gemini.go index b5e72a55..98cf8eed 100644 --- a/exchanges/gemini/gemini.go +++ b/exchanges/gemini/gemini.go @@ -14,7 +14,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common/crypto" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -58,11 +58,10 @@ const ( // AddSession, if sandbox test is needed append a new session with with the same // API keys and change the IsSandbox variable to true. type Gemini struct { - WebsocketConn *wshandler.WebsocketConnection - AuthenticatedWebsocketConn *wshandler.WebsocketConnection exchange.Base Role string RequiresHeartBeat bool + connections []stream.Connection } // GetSymbols returns all available symbols for trading diff --git a/exchanges/gemini/gemini_live_test.go b/exchanges/gemini/gemini_live_test.go index 1b75e0dd..66f4e67f 100644 --- a/exchanges/gemini/gemini_live_test.go +++ b/exchanges/gemini/gemini_live_test.go @@ -29,13 +29,12 @@ func TestMain(m *testing.M) { geminiConfig.API.Credentials.Key = apiKey geminiConfig.API.Credentials.Secret = apiSecret g.SetDefaults() + g.Websocket = sharedtestvalues.NewTestWebsocket() err = g.Setup(geminiConfig) if err != nil { log.Fatal("Gemini setup error", err) } g.API.Endpoints.URL = geminiSandboxAPIURL - g.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - g.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() log.Printf(sharedtestvalues.LiveTesting, g.Name, g.API.Endpoints.URL) os.Exit(m.Run()) } diff --git a/exchanges/gemini/gemini_mock_test.go b/exchanges/gemini/gemini_mock_test.go index 37a9b91a..0f7833b4 100644 --- a/exchanges/gemini/gemini_mock_test.go +++ b/exchanges/gemini/gemini_mock_test.go @@ -33,6 +33,7 @@ func TestMain(m *testing.M) { geminiConfig.API.Credentials.Key = apiKey geminiConfig.API.Credentials.Secret = apiSecret g.SetDefaults() + g.Websocket = sharedtestvalues.NewTestWebsocket() err = g.Setup(geminiConfig) if err != nil { log.Fatal("Gemini setup error", err) @@ -45,8 +46,6 @@ func TestMain(m *testing.M) { g.HTTPClient = newClient g.API.Endpoints.URL = serverDetails - g.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - g.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() log.Printf(sharedtestvalues.MockTesting, g.Name, g.API.Endpoints.URL) os.Exit(m.Run()) } diff --git a/exchanges/gemini/gemini_test.go b/exchanges/gemini/gemini_test.go index 1e4c509d..92f730e8 100644 --- a/exchanges/gemini/gemini_test.go +++ b/exchanges/gemini/gemini_test.go @@ -12,7 +12,7 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -550,7 +550,7 @@ func TestWsAuth(t *testing.T) { if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) + t.Skip(stream.WebsocketNotEnabled) } var dialer websocket.Dialer go g.wsReadData() @@ -571,18 +571,27 @@ func TestWsAuth(t *testing.T) { } func TestWsMissingRole(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + pressXToJSON := []byte(`{ "result":"error", "reason":"MissingRole", "message":"To access this endpoint, you need to log in to the website and go to the settings page to assign one of these roles [FundManager] to API key wujB3szN54gtJ4QDhqRJ which currently has roles [Trader]" }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err == nil { t.Error("Expected error") } } func TestWsOrderEventSubscriptionResponse(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`[ { "type" : "accepted", "order_id" : "372456298", @@ -601,7 +610,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) { "original_amount" : "14.0296", "price" : "1059.54" } ]`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } @@ -623,7 +632,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) { "price": "3592.00", "socket_sequence": 13 }]`) - err = g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } @@ -644,7 +653,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) { "total_spend": "200.00", "socket_sequence": 29 }]`) - err = g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } @@ -665,7 +674,7 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) { "original_amount": "25", "socket_sequence": 26 }]`) - err = g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } @@ -687,13 +696,17 @@ func TestWsOrderEventSubscriptionResponse(t *testing.T) { "original_amount" : "500", "socket_sequence" : 32307 } ]`) - err = g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsSubAck(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "type": "subscription_ack", "accountId": 5365, @@ -709,13 +722,17 @@ func TestWsSubAck(t *testing.T) { "closed" ] }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsHeartbeat(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "type": "heartbeat", "timestampms": 1547742998508, @@ -723,13 +740,17 @@ func TestWsHeartbeat(t *testing.T) { "trace_id": "b8biknoqppr32kc7gfgg", "socket_sequence": 37 }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsUnsubscribe(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "type": "unsubscribe", "subscriptions": [{ @@ -745,13 +766,17 @@ func TestWsUnsubscribe(t *testing.T) { ]} ] }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsTradeData(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "type": "update", "eventId": 5375547515, @@ -768,13 +793,17 @@ func TestWsTradeData(t *testing.T) { } ] }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsAuctionData(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "eventId": 371469414, "socket_sequence":4009, @@ -801,13 +830,17 @@ func TestWsAuctionData(t *testing.T) { ], "type": "update" }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsBlockTrade(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "type":"update", "eventId":1111597035, @@ -823,13 +856,17 @@ func TestWsBlockTrade(t *testing.T) { } ] }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsCandles(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "type": "candles_15m_updates", "symbol": "BTCUSD", @@ -852,13 +889,17 @@ func TestWsCandles(t *testing.T) { ] ] }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsAuctions(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "eventId": 372481811, "socket_sequence":23, @@ -875,7 +916,7 @@ func TestWsAuctions(t *testing.T) { ], "type": "update" }`) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } @@ -900,7 +941,7 @@ func TestWsAuctions(t *testing.T) { } ] }`) - err = g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } @@ -932,13 +973,17 @@ func TestWsAuctions(t *testing.T) { } ] }`) - err = g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } } func TestWsMarketData(t *testing.T) { + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } pressXToJSON := []byte(`{ "type": "update", "eventId": 5375461993, @@ -962,7 +1007,7 @@ func TestWsMarketData(t *testing.T) { } ] } `) - err := g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } @@ -990,7 +1035,7 @@ func TestWsMarketData(t *testing.T) { } ] } `) - err = g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } @@ -1012,7 +1057,7 @@ func TestWsMarketData(t *testing.T) { } ] } `) - err = g.wsHandleData(pressXToJSON, currency.NewPairFromString("BTCUSD")) + err = g.wsHandleData(pressXToJSON, pair) if err != nil { t.Error(err) } diff --git a/exchanges/gemini/gemini_websocket.go b/exchanges/gemini/gemini_websocket.go index af5d0e34..0915f38e 100644 --- a/exchanges/gemini/gemini_websocket.go +++ b/exchanges/gemini/gemini_websocket.go @@ -18,8 +18,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -39,7 +39,7 @@ var responseCheckTimeout time.Duration // WsConnect initiates a websocket connection func (g *Gemini) WsConnect() error { if !g.Websocket.IsEnabled() || !g.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer @@ -61,31 +61,36 @@ func (g *Gemini) WsConnect() error { // WsSubscribe subscribes to the full websocket suite on gemini exchange func (g *Gemini) WsSubscribe(dialer *websocket.Dialer) error { - enabledCurrencies := g.GetEnabledPairs(asset.Spot) + enabledCurrencies, err := g.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } for i := range enabledCurrencies { val := url.Values{} val.Set("heartbeat", "true") + val.Set("bids", "true") + val.Set("offers", "true") + val.Set("trades", "true") endpoint := fmt.Sprintf("%s%s/%s?%s", g.API.Endpoints.WebsocketURL, geminiWsMarketData, enabledCurrencies[i].String(), val.Encode()) - connection := &wshandler.WebsocketConnection{ - ExchangeName: g.Name, - URL: endpoint, - Verbose: g.Verbose, - ResponseCheckTimeout: responseCheckTimeout, - ResponseMaxLimit: responseMaxLimit, + connection := &stream.WebsocketConnection{ + ExchangeName: g.Name, + URL: endpoint, + Verbose: g.Verbose, + ResponseMaxLimit: responseMaxLimit, + Traffic: g.Websocket.TrafficAlert, + Match: g.Websocket.Match, } err := connection.Dial(dialer, http.Header{}) if err != nil { return fmt.Errorf("%v Websocket connection %v error. Error %v", g.Name, endpoint, err) } + g.connections = append(g.connections, connection) go g.wsFunnelConnectionData(connection, enabledCurrencies[i]) - if len(enabledCurrencies)-1 == i { - return nil - } } return nil } @@ -115,38 +120,31 @@ func (g *Gemini) WsSecureSubscribe(dialer *websocket.Dialer, url string) error { headers.Add("X-GEMINI-SIGNATURE", crypto.HexEncodeToString(hmac)) headers.Add("Cache-Control", "no-cache") - g.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: g.Name, - URL: endpoint, - Verbose: g.Verbose, - ResponseCheckTimeout: responseCheckTimeout, - ResponseMaxLimit: responseMaxLimit, + g.Websocket.AuthConn = &stream.WebsocketConnection{ + ExchangeName: g.Name, + URL: endpoint, + Verbose: g.Verbose, + ResponseMaxLimit: responseMaxLimit, + Match: g.Websocket.Match, } - err = g.AuthenticatedWebsocketConn.Dial(dialer, headers) + err = g.Websocket.AuthConn.Dial(dialer, headers) if err != nil { return fmt.Errorf("%v Websocket connection %v error. Error %v", g.Name, endpoint, err) } - go g.wsFunnelConnectionData(g.AuthenticatedWebsocketConn, currency.Pair{}) + go g.wsFunnelConnectionData(g.Websocket.AuthConn, currency.Pair{}) return nil } // wsFunnelConnectionData receives data from multiple connections and passes it to wsReadData -func (g *Gemini) wsFunnelConnectionData(ws *wshandler.WebsocketConnection, c currency.Pair) { +func (g *Gemini) wsFunnelConnectionData(ws stream.Connection, c currency.Pair) { g.Websocket.Wg.Add(1) defer g.Websocket.Wg.Done() for { - select { - case <-g.Websocket.ShutdownC: + resp := ws.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := ws.ReadMessage() - if err != nil { - g.Websocket.DataHandler <- err - return - } - g.Websocket.TrafficAlert <- struct{}{} - comms <- ReadData{Raw: resp.Raw, Currency: c} } + comms <- ReadData{Raw: resp.Raw, Currency: c} } } @@ -157,6 +155,14 @@ func (g *Gemini) wsReadData() { for { select { case <-g.Websocket.ShutdownC: + for i := range g.connections { + err := g.connections[i].Shutdown() + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + g.connections[i] = nil + } + g.connections = nil return case resp := <-comms: // Gemini likes to send empty arrays @@ -207,7 +213,16 @@ func (g *Gemini) wsHandleData(respRaw []byte, curr currency.Pair) error { Err: err, } } - p := currency.NewPairFromString(result[i].Symbol) + + p, err := currency.NewPairFromString(result[i].Symbol) + if err != nil { + g.Websocket.DataHandler <- order.ClassificationError{ + Exchange: g.Name, + OrderID: result[i].OrderID, + Err: err, + } + } + var a asset.Item a, err = g.GetPairAssetType(p) if err != nil { @@ -260,12 +275,7 @@ func (g *Gemini) wsHandleData(respRaw []byte, curr currency.Pair) error { } g.Websocket.DataHandler <- result case "heartbeat": - var result WsHeartbeatResponse - err := json.Unmarshal(respRaw, &result) - if err != nil { - return err - } - g.Websocket.DataHandler <- result + return nil case "update": if curr.IsEmpty() { return fmt.Errorf("%v - `update` response error. Currency is empty %s", @@ -290,7 +300,7 @@ func (g *Gemini) wsHandleData(respRaw []byte, curr currency.Pair) error { return err } for i := range candle.Changes { - g.Websocket.DataHandler <- wshandler.KlineData{ + g.Websocket.DataHandler <- stream.KlineData{ Timestamp: time.Unix(int64(candle.Changes[i][0])*1000, 0), Pair: curr, AssetType: asset.Spot, @@ -305,7 +315,7 @@ func (g *Gemini) wsHandleData(respRaw []byte, curr currency.Pair) error { } default: - g.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: g.Name + wshandler.UnhandledMessage + string(respRaw)} + g.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: g.Name + stream.UnhandledMessage + string(respRaw)} return nil } } else if _, ok := result["result"]; ok { @@ -318,7 +328,7 @@ func (g *Gemini) wsHandleData(respRaw []byte, curr currency.Pair) error { } return fmt.Errorf("%v Unhandled websocket error %s", g.Name, respRaw) default: - g.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: g.Name + wshandler.UnhandledMessage + string(respRaw)} + g.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: g.Name + stream.UnhandledMessage + string(respRaw)} return nil } } @@ -349,7 +359,8 @@ func stringToOrderType(oType string) (order.Type, error) { case "exchange limit", "auction-only limit", "indication-of-interest limit": return order.Limit, nil case "market buy", "market sell", "block_trade": - // block trades are conducted off order-book, so their type is market, but would be considered a hidden trade + // block trades are conducted off order-book, so their type is market, + // but would be considered a hidden trade return order.Market, nil default: return order.UnknownType, errors.New(oType + " not recognised as order type") @@ -388,9 +399,6 @@ func (g *Gemini) wsProcessUpdate(result WsMarketUpdateResponse, pair currency.Pa g.Websocket.DataHandler <- err return } - g.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{Pair: pair, - Asset: asset.Spot, - Exchange: g.Name} } else { var asks, bids []orderbook.Item for i := range result.Events { @@ -403,8 +411,8 @@ func (g *Gemini) wsProcessUpdate(result WsMarketUpdateResponse, pair currency.Pa Err: err, } } - g.Websocket.DataHandler <- wshandler.TradeData{ - Timestamp: time.Unix(0, result.Timestamp), + g.Websocket.DataHandler <- stream.TradeData{ + Timestamp: time.Unix(0, result.TimestampMS*int64(time.Millisecond)), CurrencyPair: pair, AssetType: asset.Spot, Exchange: g.Name, @@ -429,18 +437,15 @@ func (g *Gemini) wsProcessUpdate(result WsMarketUpdateResponse, pair currency.Pa if len(asks) == 0 && len(bids) == 0 { return } - err := g.Websocket.Orderbook.Update(&wsorderbook.WebsocketOrderbookUpdate{ + err := g.Websocket.Orderbook.Update(&buffer.Update{ Asks: asks, Bids: bids, Pair: pair, - UpdateTime: time.Unix(0, result.TimestampMS), + UpdateTime: time.Unix(0, result.TimestampMS*int64(time.Millisecond)), Asset: asset.Spot, }) if err != nil { g.Websocket.DataHandler <- fmt.Errorf("%v %v", g.Name, err) } - g.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{Pair: pair, - Asset: asset.Spot, - Exchange: g.Name} } } diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index 4af50c81..b555f09b 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -39,7 +39,7 @@ func (g *Gemini) GetDefaultConfig() (*config.ExchangeConfig, error) { } if g.Features.Supports.RESTCapabilities.AutoPairUpdates { - err = g.UpdateTradablePairs(true) + err := g.UpdateTradablePairs(true) if err != nil { return nil, err } @@ -56,17 +56,11 @@ func (g *Gemini) SetDefaults() { g.API.CredentialsValidator.RequiresKey = true g.API.CredentialsValidator.RequiresSecret = true - g.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Uppercase: true} + configFmt := ¤cy.PairFormat{Uppercase: true} + err := g.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } g.Features = exchange.Features{ @@ -95,8 +89,6 @@ func (g *Gemini) SetDefaults() { TradeFetching: true, AuthenticatedEndpoints: true, MessageSequenceNumbers: true, - Subscribe: true, - Unsubscribe: true, KlineFetching: true, }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithAPIPermission | @@ -115,7 +107,7 @@ func (g *Gemini) SetDefaults() { g.API.Endpoints.URLDefault = geminiAPIURL g.API.Endpoints.URL = g.API.Endpoints.URLDefault g.API.Endpoints.WebsocketURL = geminiWebsocketEndpoint - g.Websocket = wshandler.New() + g.Websocket = stream.New() g.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit g.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout g.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -137,39 +129,20 @@ func (g *Gemini) Setup(exch *config.ExchangeConfig) error { g.API.Endpoints.URL = geminiSandboxAPIURL } - err = g.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: geminiWebsocketEndpoint, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: g.WsConnect, - Features: &g.Features.Supports.WebsocketCapabilities, - }) - if err != nil { - return err - } - - g.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: g.Name, - URL: g.Websocket.GetWebsocketURL(), - ProxyURL: g.Websocket.GetProxyAddress(), - Verbose: g.Verbose, - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - g.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - true, - true, - false, - false, - exch.Name) - return nil + return g.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: geminiWebsocketEndpoint, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: g.WsConnect, + Features: &g.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + BufferEnabled: true, + SortBuffer: true, + }) } // Start starts the Gemini go routine @@ -210,7 +183,12 @@ func (g *Gemini) UpdateTradablePairs(forceUpdate bool) error { return err } - return g.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + + return g.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateAccountInfo Retrieves balances for all enabled currencies for the @@ -256,23 +234,23 @@ func (g *Gemini) FetchAccountInfo() (account.Holdings, error) { // UpdateTicker updates and returns the ticker for a currency pair func (g *Gemini) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) tick, err := g.GetTicker(p.String()) if err != nil { - return tickerPrice, err + return nil, err } - tickerPrice = &ticker.Price{ - High: tick.High, - Low: tick.Low, - Bid: tick.Bid, - Ask: tick.Ask, - Open: tick.Open, - Close: tick.Close, - Pair: p, - } - err = ticker.ProcessTicker(g.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + High: tick.High, + Low: tick.Low, + Bid: tick.Bid, + Ask: tick.Ask, + Open: tick.Open, + Close: tick.Close, + Pair: p, + ExchangeName: g.Name, + AssetType: assetType}) if err != nil { - return tickerPrice, err + return nil, err } return ticker.GetTicker(g.Name, p, assetType) @@ -347,8 +325,12 @@ func (g *Gemini) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { errors.New("only limit orders are enabled through this exchange") } - response, err := g.NewOrder( - g.FormatExchangeCurrency(s.Pair, asset.Spot).String(), + fpair, err := g.FormatExchangeCurrency(s.Pair, asset.Spot) + if err != nil { + return submitOrderResponse, err + } + + response, err := g.NewOrder(fpair.String(), s.Amount, s.Price, s.Side.String(), @@ -442,11 +424,6 @@ func (g *Gemini) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw. return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (g *Gemini) GetWebsocket() (*wshandler.Websocket, error) { - return g.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (g *Gemini) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if (!g.AllowAuthenticatedRequest() || g.SkipAuthCheck) && // Todo check connection status @@ -463,10 +440,18 @@ func (g *Gemini) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e return nil, err } + format, err := g.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range resp { - symbol := currency.NewPairDelimiter(resp[i].Symbol, - g.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(resp[i].Symbol, format.Delimiter) + if err != nil { + return nil, err + } var orderType order.Type if resp[i].Type == "exchange limit" { orderType = order.Limit @@ -507,9 +492,12 @@ func (g *Gemini) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e var trades []TradeHistory for j := range req.Pairs { - resp, err := g.GetTradeHistory(g.FormatExchangeCurrency(req.Pairs[j], - asset.Spot).String(), - req.StartTicks.Unix()) + fpair, err := g.FormatExchangeCurrency(req.Pairs[j], asset.Spot) + if err != nil { + return nil, err + } + + resp, err := g.GetTradeHistory(fpair.String(), req.StartTicks.Unix()) if err != nil { return nil, err } @@ -521,6 +509,11 @@ func (g *Gemini) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e } } + format, err := g.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range trades { side := order.Side(strings.ToUpper(trades[i].Type)) @@ -536,7 +529,7 @@ func (g *Gemini) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e Price: trades[i].Price, Pair: currency.NewPairWithDelimiter(trades[i].BaseCurrency, trades[i].QuoteCurrency, - g.GetPairFormat(asset.Spot, false).Delimiter), + format.Delimiter), }) } @@ -545,28 +538,6 @@ func (g *Gemini) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (g *Gemini) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (g *Gemini) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (g *Gemini) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (g *Gemini) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (g *Gemini) ValidateCredentials() error { diff --git a/exchanges/hitbtc/hitbtc.go b/exchanges/hitbtc/hitbtc.go index 1141ea1d..6b36f1f1 100644 --- a/exchanges/hitbtc/hitbtc.go +++ b/exchanges/hitbtc/hitbtc.go @@ -14,7 +14,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) const ( @@ -47,7 +46,6 @@ const ( // HitBTC is the overarching type across the hitbtc package type HitBTC struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } // Public Market Data diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index aaaf7b2b..f2835502 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -17,7 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -46,14 +46,11 @@ func TestMain(m *testing.M) { hitbtcConfig.API.AuthenticatedWebsocketSupport = true hitbtcConfig.API.Credentials.Key = apiKey hitbtcConfig.API.Credentials.Secret = apiSecret - + h.Websocket = sharedtestvalues.NewTestWebsocket() err = h.Setup(hitbtcConfig) if err != nil { log.Fatal("HitBTC setup error", err) } - - h.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - h.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -79,10 +76,13 @@ func TestGetChartCandles(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSD") + currencyPair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 24) end := time.Now() - _, err := h.GetHistoricCandles(currencyPair, asset.Spot, startTime, end, kline.OneMin) + _, err = h.GetHistoricCandles(currencyPair, asset.Spot, startTime, end, kline.OneMin) if err != nil { t.Fatal(err) } @@ -94,10 +94,13 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSD") + currencyPair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } startTime := time.Unix(1546300800, 0) end := time.Unix(1577836799, 0) - _, err := h.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, end, kline.OneHour) + _, err = h.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, end, kline.OneHour) if err != nil { t.Fatal(err) } @@ -142,8 +145,12 @@ func TestGetFeeByTypeOfflineTradeFee(t *testing.T) { } func TestUpdateTicker(t *testing.T) { - h.CurrencyPairs.StorePairs(asset.Spot, currency.NewPairsFromStrings([]string{"BTC-USD", "XRP-USD"}), true) - _, err := h.UpdateTicker(currency.NewPair(currency.BTC, currency.USD), asset.Spot) + pairs, err := currency.NewPairsFromStrings([]string{"BTC-USD", "XRP-USD"}) + if err != nil { + t.Fatal(err) + } + h.CurrencyPairs.StorePairs(asset.Spot, pairs, true) + _, err = h.UpdateTicker(currency.NewPair(currency.BTC, currency.USD), asset.Spot) if err != nil { t.Error(err) } @@ -438,19 +445,11 @@ func setupWsAuth(t *testing.T) { return } if !h.Websocket.IsEnabled() && !h.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) - } - h.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - h.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() - h.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: h.Name, - URL: hitbtcWebsocketAddress, - Verbose: h.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + t.Skip(stream.WebsocketNotEnabled) } + var dialer websocket.Dialer - err := h.WebsocketConn.Dial(&dialer, http.Header{}) + err := h.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } diff --git a/exchanges/hitbtc/hitbtc_types.go b/exchanges/hitbtc/hitbtc_types.go index 183c8052..38eb426a 100644 --- a/exchanges/hitbtc/hitbtc_types.go +++ b/exchanges/hitbtc/hitbtc_types.go @@ -300,23 +300,25 @@ type ResponseError struct { // WsRequest defines a request obj for the JSON-RPC and gets a websocket // response type WsRequest struct { - Method string `json:"method"` - Params interface{} `json:"params,omitempty"` - ID interface{} `json:"id"` + Method string `json:"method"` + Params Params `json:"params,omitempty"` + ID int64 `json:"id"` } // WsNotification defines a notification obj for the JSON-RPC this does not get // a websocket response type WsNotification struct { - JSONRPCVersion string `json:"jsonrpc,omitempty"` - Method string `json:"method"` - Params interface{} `json:"params"` + JSONRPCVersion string `json:"jsonrpc,omitempty"` + Method string `json:"method"` + Params Params `json:"params"` } -type params struct { - Symbol string `json:"symbol,omitempty"` - Period string `json:"period,omitempty"` - Limit int64 `json:"limit,omitempty"` +// Params is params +type Params struct { + Symbol string `json:"symbol,omitempty"` + Period string `json:"period,omitempty"` + Limit int64 `json:"limit,omitempty"` + Symbols []string `json:"symbols,omitempty"` } // WsTicker defines websocket ticker feed return params @@ -369,6 +371,7 @@ type WsTrade struct { type WsLoginRequest struct { Method string `json:"method"` Params WsLoginData `json:"params"` + ID int64 `json:"id,omitempty"` } // WsLoginData sets credentials for WsLoginRequest diff --git a/exchanges/hitbtc/hitbtc_websocket.go b/exchanges/hitbtc/hitbtc_websocket.go index a5867de1..aed37f5b 100644 --- a/exchanges/hitbtc/hitbtc_websocket.go +++ b/exchanges/hitbtc/hitbtc_websocket.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -17,9 +18,9 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/nonce" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -35,10 +36,10 @@ var requestID nonce.Nonce // WsConnect starts a new connection with the websocket API func (h *HitBTC) WsConnect() error { if !h.Websocket.IsEnabled() || !h.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := h.WebsocketConn.Dial(&dialer, http.Header{}) + err := h.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } @@ -48,35 +49,27 @@ func (h *HitBTC) WsConnect() error { log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", h.Name, err) } - h.GenerateDefaultSubscriptions() - - return nil + subs, err := h.GenerateDefaultSubscriptions() + if err != nil { + return err + } + return h.Websocket.SubscribeToChannels(subs) } // wsReadData receives and passes on websocket messages for processing func (h *HitBTC) wsReadData() { h.Websocket.Wg.Add(1) - - defer func() { - h.Websocket.Wg.Done() - }() + defer h.Websocket.Wg.Done() for { - select { - case <-h.Websocket.ShutdownC: + resp := h.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := h.WebsocketConn.ReadMessage() - if err != nil { - h.Websocket.ReadMessageErrors <- err - return - } - h.Websocket.TrafficAlert <- struct{}{} + } - err = h.wsHandleData(resp.Raw) - if err != nil { - h.Websocket.DataHandler <- err - } + err := h.wsHandleData(resp.Raw) + if err != nil { + h.Websocket.DataHandler <- err } } } @@ -91,8 +84,7 @@ func (h *HitBTC) wsGetTableName(respRaw []byte) (string, error) { h.Websocket.SetCanUseAuthenticatedEndpoints(false) } if init.ID > 0 { - if h.WebsocketConn.IsIDWaitingForResponse(init.ID) { - h.WebsocketConn.SetResponseIDAndData(init.ID, respRaw) + if h.Websocket.Match.IncomingWithData(init.ID, respRaw) { return "", nil } } @@ -132,7 +124,7 @@ func (h *HitBTC) wsGetTableName(respRaw []byte) (string, error) { return "trading", nil } } - h.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: h.Name + wshandler.UnhandledMessage + string(respRaw)} + h.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: h.Name + stream.UnhandledMessage + string(respRaw)} return "", nil } @@ -150,6 +142,24 @@ func (h *HitBTC) wsHandleData(respRaw []byte) error { if err != nil { return err } + + pairs, err := h.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + + format, err := h.GetPairFormat(asset.Spot, true) + if err != nil { + return err + } + + p, err := currency.NewPairFromFormattedPairs(wsTicker.Params.Symbol, + pairs, + format) + if err != nil { + return err + } + h.Websocket.DataHandler <- &ticker.Price{ ExchangeName: h.Name, Open: wsTicker.Params.Open, @@ -162,8 +172,7 @@ func (h *HitBTC) wsHandleData(respRaw []byte) error { Last: wsTicker.Params.Last, LastUpdated: wsTicker.Params.Timestamp, AssetType: asset.Spot, - Pair: currency.NewPairFromFormattedPairs(wsTicker.Params.Symbol, - h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)), + Pair: p, } case "snapshotOrderbook": var obSnapshot WsOrderbook @@ -252,7 +261,7 @@ func (h *HitBTC) wsHandleData(respRaw []byte) error { return err } default: - h.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: h.Name + wshandler.UnhandledMessage + string(respRaw)} + h.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: h.Name + stream.UnhandledMessage + string(respRaw)} return nil } return nil @@ -279,24 +288,28 @@ func (h *HitBTC) WsProcessOrderbookSnapshot(ob WsOrderbook) error { }) } - p := currency.NewPairFromFormattedPairs(ob.Params.Symbol, - h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)) - newOrderBook.AssetType = asset.Spot - newOrderBook.Pair = p - newOrderBook.ExchangeName = h.Name - - err := h.Websocket.Orderbook.LoadSnapshot(&newOrderBook) + pairs, err := h.GetEnabledPairs(asset.Spot) if err != nil { return err } - h.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: h.Name, - Asset: asset.Spot, - Pair: p, + format, err := h.GetPairFormat(asset.Spot, true) + if err != nil { + return err } - return nil + p, err := currency.NewPairFromFormattedPairs(ob.Params.Symbol, + pairs, + format) + if err != nil { + h.Websocket.DataHandler <- err + return err + } + newOrderBook.AssetType = asset.Spot + newOrderBook.Pair = p + newOrderBook.ExchangeName = h.Name + + return h.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } func (h *HitBTC) wsHandleOrderData(o *wsOrderData) error { @@ -336,7 +349,16 @@ func (h *HitBTC) wsHandleOrderData(o *wsOrderData) error { Err: err, } } - p := currency.NewPairFromString(o.Symbol) + + p, err := currency.NewPairFromString(o.Symbol) + if err != nil { + h.Websocket.DataHandler <- order.ClassificationError{ + Exchange: h.Name, + OrderID: o.ID, + Err: err, + } + } + var a asset.Item a, err = h.GetPairAssetType(p) if err != nil { @@ -364,7 +386,9 @@ func (h *HitBTC) wsHandleOrderData(o *wsOrderData) error { // WsProcessOrderbookUpdate updates a local cache func (h *HitBTC) WsProcessOrderbookUpdate(update WsOrderbook) error { if len(update.Params.Bid) == 0 && len(update.Params.Ask) == 0 { - return errors.New("hitbtc_websocket.go error - no data") + // Periodically HitBTC sends empty updates which includes a sequence + // can return this as nil. + return nil } var bids, asks []orderbook.Item @@ -382,105 +406,132 @@ func (h *HitBTC) WsProcessOrderbookUpdate(update WsOrderbook) error { }) } - p := currency.NewPairFromFormattedPairs(update.Params.Symbol, - h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)) - err := h.Websocket.Orderbook.Update(&wsorderbook.WebsocketOrderbookUpdate{ + pairs, err := h.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + + format, err := h.GetPairFormat(asset.Spot, true) + if err != nil { + return err + } + + p, err := currency.NewPairFromFormattedPairs(update.Params.Symbol, + pairs, + format) + if err != nil { + return err + } + + return h.Websocket.Orderbook.Update(&buffer.Update{ Asks: asks, Bids: bids, Pair: p, UpdateID: update.Params.Sequence, Asset: asset.Spot, }) - if err != nil { - return err - } +} - h.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: h.Name, - Asset: asset.Spot, - Pair: p, +// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() +func (h *HitBTC) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var channels = []string{"subscribeTicker", + "subscribeOrderbook", + "subscribeTrades", + "subscribeCandles"} + + var subscriptions []stream.ChannelSubscription + if h.Websocket.CanUseAuthenticatedEndpoints() { + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: "subscribeReports", + }) + } + enabledCurrencies, err := h.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + for i := range channels { + for j := range enabledCurrencies { + fpair, err := h.FormatExchangeCurrency(enabledCurrencies[j], asset.Spot) + if err != nil { + return nil, err + } + + enabledCurrencies[j].Delimiter = "" + subscriptions = append(subscriptions, stream.ChannelSubscription{ + Channel: channels[i], + Currency: fpair, + Asset: asset.Spot, + }) + } + } + return subscriptions, nil +} + +// Subscribe sends a websocket message to receive data from the channel +func (h *HitBTC) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + subscribe := WsRequest{ + Method: channelsToSubscribe[i].Channel, + ID: h.Websocket.Conn.GenerateMessageID(false), + } + + if channelsToSubscribe[i].Currency.String() != "" { + subscribe.Params.Symbol = channelsToSubscribe[i].Currency.String() + } + if strings.EqualFold(channelsToSubscribe[i].Channel, "subscribeTrades") { + subscribe.Params.Limit = 100 + } else if strings.EqualFold(channelsToSubscribe[i].Channel, "subscribeCandles") { + subscribe.Params.Period = "M30" + subscribe.Params.Limit = 100 + } + + err := h.Websocket.Conn.SendJSONMessage(subscribe) + if err != nil { + errs = append(errs, err) + continue + } + h.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) + } + if errs != nil { + return errs } return nil } -// GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (h *HitBTC) GenerateDefaultSubscriptions() { - var channels = []string{"subscribeTicker", "subscribeOrderbook", "subscribeTrades", "subscribeCandles"} - var subscriptions []wshandler.WebsocketChannelSubscription - if h.Websocket.CanUseAuthenticatedEndpoints() { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: "subscribeReports", - }) - } - enabledCurrencies := h.GetEnabledPairs(asset.Spot) - for i := range channels { - for j := range enabledCurrencies { - enabledCurrencies[j].Delimiter = "" - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ - Channel: channels[i], - Currency: enabledCurrencies[j], - }) - } - } - h.Websocket.SubscribeToChannels(subscriptions) -} - -// Subscribe sends a websocket message to receive data from the channel -func (h *HitBTC) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - subscribe := WsNotification{ - Method: channelToSubscribe.Channel, - } - if channelToSubscribe.Currency.String() != "" { - subscribe.Params = params{ - Symbol: h.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String(), - } - } - if strings.EqualFold(channelToSubscribe.Channel, "subscribeTrades") { - subscribe.Params = params{ - Symbol: h.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String(), - Limit: 100, - } - } else if strings.EqualFold(channelToSubscribe.Channel, "subscribeCandles") { - subscribe.Params = params{ - Symbol: h.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String(), - Period: "M30", - Limit: 100, - } - } - - return h.WebsocketConn.SendJSONMessage(subscribe) -} - // Unsubscribe sends a websocket message to stop receiving data from the channel -func (h *HitBTC) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - unsubscribeChannel := strings.Replace(channelToSubscribe.Channel, "subscribe", "unsubscribe", 1) - subscribe := WsNotification{ - JSONRPCVersion: rpcVersion, - Method: unsubscribeChannel, - Params: params{ - Symbol: h.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String(), - }, - } - if strings.EqualFold(unsubscribeChannel, "unsubscribeTrades") { - subscribe.Params = params{ - Symbol: h.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String(), - Limit: 100, - } - } else if strings.EqualFold(unsubscribeChannel, "unsubscribeCandles") { - subscribe.Params = params{ - Symbol: h.FormatExchangeCurrency(channelToSubscribe.Currency, - asset.Spot).String(), - Period: "M30", - Limit: 100, - } - } +func (h *HitBTC) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToUnsubscribe { + unsubscribeChannel := strings.Replace(channelsToUnsubscribe[i].Channel, + "subscribe", + "unsubscribe", + 1) - return h.WebsocketConn.SendJSONMessage(subscribe) + unsubscribe := WsNotification{ + JSONRPCVersion: rpcVersion, + Method: unsubscribeChannel, + } + + unsubscribe.Params.Symbol = channelsToUnsubscribe[i].Currency.String() + if strings.EqualFold(unsubscribeChannel, "unsubscribeTrades") { + unsubscribe.Params.Limit = 100 + } else if strings.EqualFold(unsubscribeChannel, "unsubscribeCandles") { + unsubscribe.Params.Period = "M30" + unsubscribe.Params.Limit = 100 + } + + err := h.Websocket.Conn.SendJSONMessage(unsubscribe) + if err != nil { + errs = append(errs, err) + continue + } + h.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) + } + if errs != nil { + return errs + } + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel @@ -499,13 +550,15 @@ func (h *HitBTC) wsLogin() error { Nonce: n, Signature: crypto.HexEncodeToString(hmac), }, + ID: h.Websocket.Conn.GenerateMessageID(false), } - err := h.WebsocketConn.SendJSONMessage(request) + err := h.Websocket.Conn.SendJSONMessage(request) if err != nil { h.Websocket.SetCanUseAuthenticatedEndpoints(false) return err } + return nil } @@ -514,19 +567,25 @@ func (h *HitBTC) wsPlaceOrder(pair currency.Pair, side string, price, quantity f if !h.Websocket.CanUseAuthenticatedEndpoints() { return nil, fmt.Errorf("%v not authenticated, cannot place order", h.Name) } - id := h.WebsocketConn.GenerateMessageID(false) + + id := h.Websocket.Conn.GenerateMessageID(false) + fpair, err := h.FormatExchangeCurrency(pair, asset.Spot) + if err != nil { + return nil, err + } + request := WsSubmitOrderRequest{ Method: "newOrder", Params: WsSubmitOrderRequestData{ ClientOrderID: id, - Symbol: h.FormatExchangeCurrency(pair, asset.Spot).String(), + Symbol: fpair.String(), Side: strings.ToLower(side), Price: price, Quantity: quantity, }, ID: id, } - resp, err := h.WebsocketConn.SendMessageReturnResponse(id, request) + resp, err := h.Websocket.Conn.SendMessageReturnResponse(id, request) if err != nil { return nil, fmt.Errorf("%v %v", h.Name, err) } @@ -551,9 +610,9 @@ func (h *HitBTC) wsCancelOrder(clientOrderID string) (*WsCancelOrderResponse, er Params: WsCancelOrderRequestData{ ClientOrderID: clientOrderID, }, - ID: h.WebsocketConn.GenerateMessageID(false), + ID: h.Websocket.Conn.GenerateMessageID(false), } - resp, err := h.WebsocketConn.SendMessageReturnResponse(request.ID, request) + resp, err := h.Websocket.Conn.SendMessageReturnResponse(request.ID, request) if err != nil { return nil, fmt.Errorf("%v %v", h.Name, err) } @@ -581,9 +640,9 @@ func (h *HitBTC) wsReplaceOrder(clientOrderID string, quantity, price float64) ( Quantity: quantity, Price: price, }, - ID: h.WebsocketConn.GenerateMessageID(false), + ID: h.Websocket.Conn.GenerateMessageID(false), } - resp, err := h.WebsocketConn.SendMessageReturnResponse(request.ID, request) + resp, err := h.Websocket.Conn.SendMessageReturnResponse(request.ID, request) if err != nil { return nil, fmt.Errorf("%v %v", h.Name, err) } @@ -601,14 +660,14 @@ func (h *HitBTC) wsReplaceOrder(clientOrderID string, quantity, price float64) ( // wsGetActiveOrders sends a websocket message to get all active orders func (h *HitBTC) wsGetActiveOrders() (*wsActiveOrdersResponse, error) { if !h.Websocket.CanUseAuthenticatedEndpoints() { - return nil, fmt.Errorf("%v not authenticated, cannot place order", h.Name) + return nil, fmt.Errorf("%v not authenticated, cannot get active orders", h.Name) } request := WsReplaceOrderRequest{ Method: "getOrders", Params: WsReplaceOrderRequestData{}, - ID: h.WebsocketConn.GenerateMessageID(false), + ID: h.Websocket.Conn.GenerateMessageID(false), } - resp, err := h.WebsocketConn.SendMessageReturnResponse(request.ID, request) + resp, err := h.Websocket.Conn.SendMessageReturnResponse(request.ID, request) if err != nil { return nil, fmt.Errorf("%v %v", h.Name, err) } @@ -631,9 +690,9 @@ func (h *HitBTC) wsGetTradingBalance() (*WsGetTradingBalanceResponse, error) { request := WsReplaceOrderRequest{ Method: "getTradingBalance", Params: WsReplaceOrderRequestData{}, - ID: h.WebsocketConn.GenerateMessageID(false), + ID: h.Websocket.Conn.GenerateMessageID(false), } - resp, err := h.WebsocketConn.SendMessageReturnResponse(request.ID, request) + resp, err := h.Websocket.Conn.SendMessageReturnResponse(request.ID, request) if err != nil { return nil, fmt.Errorf("%v %v", h.Name, err) } @@ -655,9 +714,9 @@ func (h *HitBTC) wsGetCurrencies(currencyItem currency.Code) (*WsGetCurrenciesRe Params: WsGetCurrenciesRequestParameters{ Currency: currencyItem, }, - ID: h.WebsocketConn.GenerateMessageID(false), + ID: h.Websocket.Conn.GenerateMessageID(false), } - resp, err := h.WebsocketConn.SendMessageReturnResponse(request.ID, request) + resp, err := h.Websocket.Conn.SendMessageReturnResponse(request.ID, request) if err != nil { return nil, fmt.Errorf("%v %v", h.Name, err) } @@ -673,15 +732,20 @@ func (h *HitBTC) wsGetCurrencies(currencyItem currency.Code) (*WsGetCurrenciesRe } // wsGetSymbols sends a websocket message to get trading balance -func (h *HitBTC) wsGetSymbols(currencyItem currency.Pair) (*WsGetSymbolsResponse, error) { +func (h *HitBTC) wsGetSymbols(c currency.Pair) (*WsGetSymbolsResponse, error) { + fpair, err := h.FormatExchangeCurrency(c, asset.Spot) + if err != nil { + return nil, err + } + request := WsGetSymbolsRequest{ Method: "getSymbol", Params: WsGetSymbolsRequestParameters{ - Symbol: h.FormatExchangeCurrency(currencyItem, asset.Spot).String(), + Symbol: fpair.String(), }, - ID: h.WebsocketConn.GenerateMessageID(false), + ID: h.Websocket.Conn.GenerateMessageID(false), } - resp, err := h.WebsocketConn.SendMessageReturnResponse(request.ID, request) + resp, err := h.Websocket.Conn.SendMessageReturnResponse(request.ID, request) if err != nil { return nil, fmt.Errorf("%v %v", h.Name, err) } @@ -697,18 +761,23 @@ func (h *HitBTC) wsGetSymbols(currencyItem currency.Pair) (*WsGetSymbolsResponse } // wsGetSymbols sends a websocket message to get trading balance -func (h *HitBTC) wsGetTrades(currencyItem currency.Pair, limit int64, sort, by string) (*WsGetTradesResponse, error) { +func (h *HitBTC) wsGetTrades(c currency.Pair, limit int64, sort, by string) (*WsGetTradesResponse, error) { + fpair, err := h.FormatExchangeCurrency(c, asset.Spot) + if err != nil { + return nil, err + } + request := WsGetTradesRequest{ Method: "getTrades", Params: WsGetTradesRequestParameters{ - Symbol: h.FormatExchangeCurrency(currencyItem, asset.Spot).String(), + Symbol: fpair.String(), Limit: limit, Sort: sort, By: by, }, - ID: h.WebsocketConn.GenerateMessageID(false), + ID: h.Websocket.Conn.GenerateMessageID(false), } - resp, err := h.WebsocketConn.SendMessageReturnResponse(request.ID, request) + resp, err := h.Websocket.Conn.SendMessageReturnResponse(request.ID, request) if err != nil { return nil, fmt.Errorf("%v %v", h.Name, err) } diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index cfbdcb32..f23872d2 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,18 +56,11 @@ func (h *HitBTC) SetDefaults() { h.API.CredentialsValidator.RequiresKey = true h.API.CredentialsValidator.RequiresSecret = true - h.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "-", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Uppercase: true} + configFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + err := h.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } h.Features = exchange.Features{ @@ -139,7 +132,7 @@ func (h *HitBTC) SetDefaults() { h.API.Endpoints.URLDefault = apiURL h.API.Endpoints.URL = h.API.Endpoints.URLDefault h.API.Endpoints.WebsocketURL = hitbtcWebsocketAddress - h.Websocket = wshandler.New() + h.Websocket = stream.New() h.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit h.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout h.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -157,42 +150,33 @@ func (h *HitBTC) Setup(exch *config.ExchangeConfig) error { return err } - err = h.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: hitbtcWebsocketAddress, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: h.WsConnect, - Subscriber: h.Subscribe, - UnSubscriber: h.Unsubscribe, - Features: &h.Features.Supports.WebsocketCapabilities, - }) + err = h.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: hitbtcWebsocketAddress, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: h.WsConnect, + Subscriber: h.Subscribe, + UnSubscriber: h.Unsubscribe, + GenerateSubscriptions: h.GenerateDefaultSubscriptions, + Features: &h.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + BufferEnabled: true, + SortBuffer: true, + SortBufferByUpdateIDs: true, + }) if err != nil { return err } - h.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: h.Name, - URL: h.Websocket.GetWebsocketURL(), - ProxyURL: h.Websocket.GetProxyAddress(), - Verbose: h.Verbose, + return h.Websocket.SetupNewConnection(stream.ConnectionSetup{ RateLimit: rateLimit, ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - h.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - true, - true, - true, - false, - exch.Name) - return nil + }) } // Start starts the HitBTC go routine @@ -212,16 +196,52 @@ func (h *HitBTC) Run() { } forceUpdate := false - delim := h.GetPairFormat(asset.Spot, false).Delimiter - if !common.StringDataContains(h.GetEnabledPairs(asset.Spot).Strings(), delim) || - !common.StringDataContains(h.GetAvailablePairs(asset.Spot).Strings(), delim) { - enabledPairs := []string{currency.BTC.String() + delim + currency.USD.String()} - log.Warn(log.ExchangeSys, "Available pairs for HitBTC reset due to config upgrade, please enable the ones you would like again.") - forceUpdate = true + format, err := h.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + h.Name, + err) + return + } + enabled, err := h.GetEnabledPairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + h.Name, + err) + return + } - err := h.UpdatePairs(currency.NewPairsFromStrings(enabledPairs), asset.Spot, true, true) + avail, err := h.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + h.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + enabledPairs := []string{currency.BTC.String() + format.Delimiter + currency.USD.String()} + log.Warn(log.ExchangeSys, + "Available pairs for HitBTC reset due to config upgrade, please enable the ones you would like again.") + forceUpdate = true + var p currency.Pairs + p, err = currency.NewPairsFromStrings(enabledPairs) if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to update enabled currencies.\n", h.Name) + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + h.Name, + err) + return + } + err = h.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update enabled currencies.\n", + h.Name) } } @@ -229,9 +249,12 @@ func (h *HitBTC) Run() { return } - err := h.UpdateTradablePairs(forceUpdate) + err = h.UpdateTradablePairs(forceUpdate) if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", h.Name, err) + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + h.Name, + err) } } @@ -242,10 +265,16 @@ func (h *HitBTC) FetchTradablePairs(asset asset.Item) ([]string, error) { return nil, err } + format, err := h.GetPairFormat(asset, false) + if err != nil { + return nil, err + } + var pairs []string for x := range symbols { pairs = append(pairs, symbols[x].BaseCurrency+ - h.GetPairFormat(asset, false).Delimiter+symbols[x].QuoteCurrency) + format.Delimiter+ + symbols[x].QuoteCurrency) } return pairs, nil } @@ -258,24 +287,34 @@ func (h *HitBTC) UpdateTradablePairs(forceUpdate bool) error { return err } - return h.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return h.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair -func (h *HitBTC) UpdateTicker(currencyPair currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) +func (h *HitBTC) UpdateTicker(p currency.Pair, a asset.Item) (*ticker.Price, error) { tick, err := h.GetTickers() if err != nil { - return tickerPrice, err + return nil, err + } + pairs, err := h.GetEnabledPairs(a) + if err != nil { + return nil, err } - pairs := h.GetEnabledPairs(assetType) for i := range pairs { for j := range tick { - pairFmt := h.FormatExchangeCurrency(pairs[i], assetType).String() - if tick[j].Symbol != pairFmt { + pairFmt, err := h.FormatExchangeCurrency(pairs[i], a) + if err != nil { + return nil, err + } + + if tick[j].Symbol != pairFmt.String() { found := false if strings.Contains(tick[j].Symbol, "USDT") { - if pairFmt == tick[j].Symbol[0:len(tick[j].Symbol)-1] { + if pairFmt.String() == tick[j].Symbol[0:len(tick[j].Symbol)-1] { found = true } } @@ -283,25 +322,26 @@ func (h *HitBTC) UpdateTicker(currencyPair currency.Pair, assetType asset.Item) continue } } - tickerPrice := &ticker.Price{ - Last: tick[j].Last, - High: tick[j].High, - Low: tick[j].Low, - Bid: tick[j].Bid, - Ask: tick[j].Ask, - Volume: tick[j].Volume, - QuoteVolume: tick[j].VolumeQuote, - Open: tick[j].Open, - Pair: pairs[i], - LastUpdated: tick[j].Timestamp, - } - err = ticker.ProcessTicker(h.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick[j].Last, + High: tick[j].High, + Low: tick[j].Low, + Bid: tick[j].Bid, + Ask: tick[j].Ask, + Volume: tick[j].Volume, + QuoteVolume: tick[j].VolumeQuote, + Open: tick[j].Open, + Pair: pairs[i], + LastUpdated: tick[j].Timestamp, + ExchangeName: h.Name, + AssetType: a}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } - return ticker.GetTicker(h.Name, currencyPair, assetType) + return ticker.GetTicker(h.Name, p, a) } // FetchTicker returns the ticker for a currency pair @@ -323,13 +363,18 @@ func (h *HitBTC) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbo } // UpdateOrderbook updates and returns the orderbook for a currency pair -func (h *HitBTC) UpdateOrderbook(currencyPair currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - orderbookNew, err := h.GetOrderbook(h.FormatExchangeCurrency(currencyPair, assetType).String(), 1000) +func (h *HitBTC) UpdateOrderbook(c currency.Pair, assetType asset.Item) (*orderbook.Base, error) { + fpair, err := h.FormatExchangeCurrency(c, assetType) if err != nil { - return orderBook, err + return nil, err } + orderbookNew, err := h.GetOrderbook(fpair.String(), 1000) + if err != nil { + return nil, err + } + + orderBook := new(orderbook.Base) for x := range orderbookNew.Bids { orderBook.Bids = append(orderBook.Bids, orderbook.Item{ Amount: orderbookNew.Bids[x].Amount, @@ -344,7 +389,7 @@ func (h *HitBTC) UpdateOrderbook(currencyPair currency.Pair, assetType asset.Ite }) } - orderBook.Pair = currencyPair + orderBook.Pair = c orderBook.ExchangeName = h.Name orderBook.AssetType = assetType @@ -353,7 +398,7 @@ func (h *HitBTC) UpdateOrderbook(currencyPair currency.Pair, assetType asset.Ite return orderBook, err } - return orderbook.Get(h.Name, currencyPair, assetType) + return orderbook.Get(h.Name, c, assetType) } // UpdateAccountInfo retrieves balances for all enabled currencies for the @@ -527,11 +572,6 @@ func (h *HitBTC) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw. return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (h *HitBTC) GetWebsocket() (*wshandler.Websocket, error) { - return h.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (h *HitBTC) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !h.AllowAuthenticatedRequest() && // Todo check connection status @@ -556,10 +596,19 @@ func (h *HitBTC) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e allOrders = append(allOrders, resp...) } + format, err := h.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range allOrders { - symbol := currency.NewPairDelimiter(allOrders[i].Symbol, - h.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(allOrders[i].Symbol, + format.Delimiter) + if err != nil { + return nil, err + } side := order.Side(strings.ToUpper(allOrders[i].Side)) orders = append(orders, order.Detail{ ID: allOrders[i].ID, @@ -593,10 +642,19 @@ func (h *HitBTC) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e allOrders = append(allOrders, resp...) } + format, err := h.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range allOrders { - symbol := currency.NewPairDelimiter(allOrders[i].Symbol, - h.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(allOrders[i].Symbol, + format.Delimiter) + if err != nil { + return nil, err + } side := order.Side(strings.ToUpper(allOrders[i].Side)) orders = append(orders, order.Detail{ ID: allOrders[i].ID, @@ -614,25 +672,6 @@ func (h *HitBTC) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, e return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (h *HitBTC) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - h.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (h *HitBTC) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - h.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (h *HitBTC) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return h.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (h *HitBTC) AuthenticateWebsocket() error { return h.wsLogin() @@ -666,7 +705,13 @@ func (h *HitBTC) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end Interval: interval, } } - data, err := h.GetCandles(h.FormatExchangeCurrency(pair, a).String(), + + formattedPair, err := h.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + + data, err := h.GetCandles(formattedPair.String(), strconv.FormatInt(int64(h.Features.Enabled.Kline.ResultLimit), 10), h.FormatExchangeKlineInterval(interval), start, end) @@ -711,8 +756,13 @@ func (h *HitBTC) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, st } dates := kline.CalcDateRanges(start, end, interval, h.Features.Enabled.Kline.ResultLimit) + formattedPair, err := h.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + for y := range dates { - data, err := h.GetCandles(h.FormatExchangeCurrency(pair, a).String(), + data, err := h.GetCandles(formattedPair.String(), strconv.FormatInt(int64(h.Features.Enabled.Kline.ResultLimit), 10), h.FormatExchangeKlineInterval(interval), dates[y].Start, dates[y].End) diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go index 3f543fae..c0fc5d8a 100644 --- a/exchanges/huobi/huobi.go +++ b/exchanges/huobi/huobi.go @@ -16,7 +16,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) const ( @@ -62,9 +61,7 @@ const ( // HUOBI is the overarching type across this package type HUOBI struct { exchange.Base - AccountID string - WebsocketConn *wshandler.WebsocketConnection - AuthenticatedWebsocketConn *wshandler.WebsocketConnection + AccountID string } // GetSpotKline returns kline data diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 6bf3148b..03d692c1 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -17,7 +17,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -47,13 +47,11 @@ func TestMain(m *testing.M) { hConfig.API.AuthenticatedWebsocketSupport = true hConfig.API.Credentials.Key = apiKey hConfig.API.Credentials.Secret = apiSecret - + h.Websocket = sharedtestvalues.NewTestWebsocket() err = h.Setup(hConfig) if err != nil { log.Fatal("Huobi setup error", err) } - h.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - h.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -62,26 +60,10 @@ func setupWsTests(t *testing.T) { return } if !h.Websocket.IsEnabled() && !h.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) + t.Skip(stream.WebsocketNotEnabled) } comms = make(chan WsMessage, sharedtestvalues.WebsocketChannelOverrideCapacity) - h.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - h.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() go h.wsReadData() - h.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: h.Name, - URL: wsAccountsOrdersURL, - Verbose: h.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, - } - h.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: h.Name, - URL: wsMarketURL, - Verbose: h.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, - } var dialer websocket.Dialer err := h.wsAuthenticatedDial(&dialer) if err != nil { @@ -108,9 +90,12 @@ func TestGetSpotKline(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSDT") + currencyPair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 1) - _, err := h.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = h.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } @@ -127,9 +112,12 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSDT") + currencyPair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 1) - _, err := h.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = h.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } @@ -663,24 +651,22 @@ func TestQueryWithdrawQuota(t *testing.T) { // TestWsGetAccountsList connects to WS, logs in, gets account list func TestWsGetAccountsList(t *testing.T) { setupWsTests(t) - resp, err := h.wsGetAccountsList() + _, err := h.wsGetAccountsList() if err != nil { t.Fatal(err) } - if resp.ErrorCode > 0 { - t.Error(resp.ErrorMessage) - } } // TestWsGetOrderList connects to WS, logs in, gets order list func TestWsGetOrderList(t *testing.T) { setupWsTests(t) - resp, err := h.wsGetOrdersList(1, currency.NewPairFromString("ethbtc")) + p, err := currency.NewPairFromString("ethbtc") if err != nil { t.Fatal(err) } - if resp.ErrorCode > 0 { - t.Error(resp.ErrorMessage) + _, err = h.wsGetOrdersList(1, p) + if err != nil { + t.Fatal(err) } } @@ -688,13 +674,10 @@ func TestWsGetOrderList(t *testing.T) { func TestWsGetOrderDetails(t *testing.T) { setupWsTests(t) orderID := "123" - resp, err := h.wsGetOrderDetails(orderID) + _, err := h.wsGetOrderDetails(orderID) if err != nil { t.Fatal(err) } - if resp.ErrorCode > 0 && resp.ErrorCode != 10022 { - t.Error(resp.ErrorMessage) - } } func TestWsSubResponse(t *testing.T) { diff --git a/exchanges/huobi/huobi_types.go b/exchanges/huobi/huobi_types.go index b2e6cdb6..b435b0c3 100644 --- a/exchanges/huobi/huobi_types.go +++ b/exchanges/huobi/huobi_types.go @@ -315,18 +315,19 @@ type WsRequest struct { // WsResponse defines a response from the websocket connection when there // is an error type WsResponse struct { - Op string `json:"op"` - TS int64 `json:"ts"` - Status string `json:"status"` - ErrorCode int64 `json:"err-code"` - ErrorMessage string `json:"err-msg"` - Ping int64 `json:"ping"` - Channel string `json:"ch"` - Rep string `json:"rep"` - Topic string `json:"topic"` - Subscribed string `json:"subbed"` - UnSubscribed string `json:"unsubbed"` - ClientID int64 `json:"cid,string"` + Op string `json:"op"` + TS int64 `json:"ts"` + Status string `json:"status"` + // ErrorCode returns either an integer or a string + ErrorCode interface{} `json:"err-code"` + ErrorMessage string `json:"err-msg"` + Ping int64 `json:"ping"` + Channel string `json:"ch"` + Rep string `json:"rep"` + Topic string `json:"topic"` + Subscribed string `json:"subbed"` + UnSubscribed string `json:"unsubbed"` + ClientID int64 `json:"cid,string"` } // WsHeartBeat defines a heartbeat request @@ -359,7 +360,7 @@ type WsKline struct { Amount float64 `json:"amount"` Volume float64 `json:"vol"` Count int64 `json:"count"` - } + } `json:"tick"` } // WsTick stores websocket ticker data @@ -577,7 +578,7 @@ type WsPong struct { Pong int64 `json:"pong"` } -type wsKLineResponseThing struct { +type wsKlineResponse struct { Data []struct { Amount float64 `json:"amount"` Close float64 `json:"close"` @@ -591,3 +592,8 @@ type wsKLineResponseThing struct { Rep string `json:"rep"` Status string `json:"status"` } + +type authenticationPing struct { + OP string `json:"op"` + TS int64 `json:"ts"` +} diff --git a/exchanges/huobi/huobi_websocket.go b/exchanges/huobi/huobi_websocket.go index 4f773411..9bebc259 100644 --- a/exchanges/huobi/huobi_websocket.go +++ b/exchanges/huobi/huobi_websocket.go @@ -11,14 +11,15 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -57,7 +58,7 @@ var comms = make(chan WsMessage) // WsConnect initiates a new websocket connection func (h *HUOBI) WsConnect() error { if !h.Websocket.IsEnabled() || !h.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer err := h.wsDial(&dialer) @@ -66,58 +67,61 @@ func (h *HUOBI) WsConnect() error { } err = h.wsAuthenticatedDial(&dialer) if err != nil { - log.Errorf(log.ExchangeSys, "%v - authenticated dial failed: %v\n", h.Name, err) + log.Errorf(log.ExchangeSys, + "%v - authenticated dial failed: %v\n", + h.Name, + err) } err = h.wsLogin() if err != nil { - log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", h.Name, err) + log.Errorf(log.ExchangeSys, + "%v - authentication failed: %v\n", + h.Name, + err) h.Websocket.SetCanUseAuthenticatedEndpoints(false) } go h.wsReadData() - h.GenerateDefaultSubscriptions() - - return nil -} - -func (h *HUOBI) wsDial(dialer *websocket.Dialer) error { - err := h.WebsocketConn.Dial(dialer, http.Header{}) + subs, err := h.GenerateDefaultSubscriptions() if err != nil { return err } - go h.wsFunnelConnectionData(h.WebsocketConn, wsMarketURL) + return h.Websocket.SubscribeToChannels(subs) +} + +func (h *HUOBI) wsDial(dialer *websocket.Dialer) error { + err := h.Websocket.Conn.Dial(dialer, http.Header{}) + if err != nil { + return err + } + go h.wsFunnelConnectionData(h.Websocket.Conn, wsMarketURL) return nil } func (h *HUOBI) wsAuthenticatedDial(dialer *websocket.Dialer) error { if !h.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", h.Name) + return fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", + h.Name) } - err := h.AuthenticatedWebsocketConn.Dial(dialer, http.Header{}) + err := h.Websocket.AuthConn.Dial(dialer, http.Header{}) if err != nil { return err } - go h.wsFunnelConnectionData(h.AuthenticatedWebsocketConn, wsAccountsOrdersURL) + go h.wsFunnelConnectionData(h.Websocket.AuthConn, wsAccountsOrdersURL) return nil } -// wsFunnelConnectionData manages data from multiple endpoints and passes it to a channel -func (h *HUOBI) wsFunnelConnectionData(ws *wshandler.WebsocketConnection, url string) { +// wsFunnelConnectionData manages data from multiple endpoints and passes it to +// a channel +func (h *HUOBI) wsFunnelConnectionData(ws stream.Connection, url string) { h.Websocket.Wg.Add(1) defer h.Websocket.Wg.Done() for { - select { - case <-h.Websocket.ShutdownC: + resp := ws.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := ws.ReadMessage() - if err != nil { - h.Websocket.DataHandler <- err - return - } - h.Websocket.TrafficAlert <- struct{}{} - comms <- WsMessage{Raw: resp.Raw, URL: url} } + comms <- WsMessage{Raw: resp.Raw, URL: url} } } @@ -126,14 +130,10 @@ func (h *HUOBI) wsReadData() { h.Websocket.Wg.Add(1) defer h.Websocket.Wg.Done() for { - select { - case <-h.Websocket.ShutdownC: - return - case resp := <-comms: - err := h.wsHandleData(resp.Raw) - if err != nil { - h.Websocket.DataHandler <- err - } + resp := <-comms + err := h.wsHandleData(resp.Raw) + if err != nil { + h.Websocket.DataHandler <- err } } } @@ -149,7 +149,8 @@ func stringToOrderStatus(status string) (order.Status, error) { case "partial-canceled": return order.PartiallyCancelled, nil default: - return order.UnknownStatus, errors.New(status + " not recognised as order status") + return order.UnknownStatus, + errors.New(status + " not recognised as order status") } } @@ -161,7 +162,8 @@ func stringToOrderSide(side string) (order.Side, error) { return order.Sell, nil } - return order.UnknownSide, errors.New(side + " not recognised as order side") + return order.UnknownSide, + errors.New(side + " not recognised as order side") } func stringToOrderType(oType string) (order.Type, error) { @@ -172,7 +174,8 @@ func stringToOrderType(oType string) (order.Type, error) { return order.Market, nil } - return order.UnknownType, errors.New(oType + " not recognised as order type") + return order.UnknownType, + errors.New(oType + " not recognised as order type") } func (h *HUOBI) wsHandleData(respRaw []byte) error { @@ -192,13 +195,32 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { h.sendPingResponse(init.Ping) return nil } - if init.ErrorMessage == "api-signature-not-valid" { - h.Websocket.SetCanUseAuthenticatedEndpoints(false) - return errors.New(h.Name + " - invalid credentials. Authenticated requests disabled") + + if init.Op == "ping" { + authPing := authenticationPing{ + OP: "pong", + TS: init.TS, + } + err := h.Websocket.AuthConn.SendJSONMessage(authPing) + if err != nil { + log.Error(log.ExchangeSys, err) + } + return nil } + + if init.ErrorMessage != "" { + if init.ErrorMessage == "api-signature-not-valid" { + h.Websocket.SetCanUseAuthenticatedEndpoints(false) + return errors.New(h.Name + + " - invalid credentials. Authenticated requests disabled") + } + + codes, _ := init.ErrorCode.(string) + return errors.New(h.Name + " Code:" + codes + " Message:" + init.ErrorMessage) + } + if init.ClientID > 0 { - if h.WebsocketConn.IsIDWaitingForResponse(init.ClientID) { - h.WebsocketConn.SetResponseIDAndData(init.ClientID, respRaw) + if h.Websocket.Match.IncomingWithData(init.ClientID, respRaw) { return nil } } @@ -206,12 +228,8 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { switch { case strings.EqualFold(init.Op, authOp): h.Websocket.SetCanUseAuthenticatedEndpoints(true) - err := json.Unmarshal(respRaw, &init) - if err != nil { - return err - } - h.Websocket.DataHandler <- init - + // Auth captured + return nil case strings.EqualFold(init.Topic, "accounts"): var response WsAuthenticatedAccountsResponse err := json.Unmarshal(respRaw, &response) @@ -229,7 +247,8 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { } data := strings.Split(response.Topic, ".") if len(data) < 2 { - return errors.New(h.Name + " - currency could not be extracted from response") + return errors.New(h.Name + + " - currency could not be extracted from response") } orderID := strconv.FormatInt(response.Data.OrderID, 10) var oSide order.Side @@ -299,27 +318,6 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { if err != nil { return err } - case strings.Contains(init.Rep, "kline"): - var kline wsKLineResponseThing - err := json.Unmarshal(respRaw, &kline) - if err != nil { - return err - } - var curr = strings.Split(init.Rep, ".") - for i := range kline.Data { - h.Websocket.DataHandler <- wshandler.KlineData{ - Timestamp: time.Now(), - Exchange: h.Name, - AssetType: asset.Spot, - Pair: currency.NewPairFromFormattedPairs(curr[1], - h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)), - OpenPrice: kline.Data[i].Open, - ClosePrice: kline.Data[i].Close, - HighPrice: kline.Data[i].High, - LowPrice: kline.Data[i].Low, - Volume: kline.Data[i].Volume, - } - } case strings.Contains(init.Channel, "kline"): var kline WsKline err := json.Unmarshal(respRaw, &kline) @@ -327,17 +325,23 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { return err } data := strings.Split(kline.Channel, ".") - h.Websocket.DataHandler <- wshandler.KlineData{ - Timestamp: time.Unix(0, kline.Timestamp*int64(time.Millisecond)), - Exchange: h.Name, - AssetType: asset.Spot, - Pair: currency.NewPairFromFormattedPairs(data[1], - h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)), + var p currency.Pair + var a asset.Item + p, a, err = h.GetRequestFormattedPairAndAssetType(data[1]) + if err != nil { + return err + } + h.Websocket.DataHandler <- stream.KlineData{ + Timestamp: time.Unix(0, kline.Timestamp*int64(time.Millisecond)), + Exchange: h.Name, + AssetType: a, + Pair: p, OpenPrice: kline.Tick.Open, ClosePrice: kline.Tick.Close, HighPrice: kline.Tick.High, LowPrice: kline.Tick.Low, Volume: kline.Tick.Volume, + Interval: data[3], } case strings.Contains(init.Channel, "trade.detail"): var trade WsTrade @@ -346,12 +350,28 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { return err } data := strings.Split(trade.Channel, ".") - h.Websocket.DataHandler <- wshandler.TradeData{ - Exchange: h.Name, - AssetType: asset.Spot, - CurrencyPair: currency.NewPairFromFormattedPairs(data[1], - h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)), - Timestamp: time.Unix(0, trade.Tick.Timestamp*int64(time.Millisecond)), + var p currency.Pair + var a asset.Item + p, a, err = h.GetRequestFormattedPairAndAssetType(data[1]) + if err != nil { + return err + } + + for i := range trade.Tick.Data { + side := order.Buy + if trade.Tick.Data[i].Direction != "buy" { + side = order.Sell + } + h.Websocket.DataHandler <- stream.TradeData{ + Exchange: h.Name, + AssetType: a, + CurrencyPair: p, + Timestamp: time.Unix(0, + trade.Tick.Data[i].Timestamp*int64(time.Millisecond)), + Amount: trade.Tick.Data[i].Amount, + Price: trade.Tick.Data[i].Price, + Side: side, + } } case strings.Contains(init.Channel, "detail"), strings.Contains(init.Rep, "detail"): @@ -367,6 +387,14 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { if wsTicker.Rep != "" { data = strings.Split(wsTicker.Rep, ".") } + + var p currency.Pair + var a asset.Item + p, a, err = h.GetRequestFormattedPairAndAssetType(data[1]) + if err != nil { + return err + } + h.Websocket.DataHandler <- &ticker.Price{ ExchangeName: h.Name, Open: wsTicker.Tick.Open, @@ -376,19 +404,20 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { High: wsTicker.Tick.High, Low: wsTicker.Tick.Low, LastUpdated: time.Unix(0, wsTicker.Timestamp*int64(time.Millisecond)), - AssetType: asset.Spot, - Pair: currency.NewPairFromFormattedPairs(data[1], - h.GetEnabledPairs(asset.Spot), h.GetPairFormat(asset.Spot, true)), + AssetType: a, + Pair: p, } default: - h.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: h.Name + wshandler.UnhandledMessage + string(respRaw)} + h.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: h.Name + stream.UnhandledMessage + string(respRaw), + } return nil } return nil } func (h *HUOBI) sendPingResponse(pong int64) { - err := h.WebsocketConn.SendJSONMessage(WsPong{Pong: pong}) + err := h.Websocket.Conn.SendJSONMessage(WsPong{Pong: pong}) if err != nil { log.Error(log.ExchangeSys, err) } @@ -396,9 +425,22 @@ func (h *HUOBI) sendPingResponse(pong int64) { // WsProcessOrderbook processes new orderbook data func (h *HUOBI) WsProcessOrderbook(update *WsDepth, symbol string) error { - p := currency.NewPairFromFormattedPairs(symbol, - h.GetEnabledPairs(asset.Spot), - h.GetPairFormat(asset.Spot, true)) + pairs, err := h.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + + format, err := h.GetPairFormat(asset.Spot, true) + if err != nil { + return err + } + + p, err := currency.NewPairFromFormattedPairs(symbol, + pairs, + format) + if err != nil { + return err + } var bids, asks []orderbook.Item for i := range update.Tick.Bids { @@ -422,59 +464,100 @@ func (h *HUOBI) WsProcessOrderbook(update *WsDepth, symbol string) error { newOrderBook.AssetType = asset.Spot newOrderBook.ExchangeName = h.Name - err := h.Websocket.Orderbook.LoadSnapshot(&newOrderBook) - if err != nil { - return err - } - - h.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: p, - Exchange: h.Name, - Asset: asset.Spot, - } - return nil + return h.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (h *HUOBI) GenerateDefaultSubscriptions() { - var channels = []string{wsMarketKline, wsMarketDepth, wsMarketTrade, wsMarketTicker} - var subscriptions []wshandler.WebsocketChannelSubscription +func (h *HUOBI) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var channels = []string{wsMarketKline, + wsMarketDepth, + wsMarketTrade, + wsMarketTicker} + var subscriptions []stream.ChannelSubscription if h.Websocket.CanUseAuthenticatedEndpoints() { channels = append(channels, "orders.%v", "orders.%v.update") - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: "accounts", }) } - enabledCurrencies := h.GetEnabledPairs(asset.Spot) + enabledCurrencies, err := h.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } for i := range channels { for j := range enabledCurrencies { enabledCurrencies[j].Delimiter = "" - channel := fmt.Sprintf(channels[i], enabledCurrencies[j].Lower().String()) - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + channel := fmt.Sprintf(channels[i], + enabledCurrencies[j].Lower().String()) + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channel, Currency: enabledCurrencies[j], }) } } - h.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (h *HUOBI) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - if strings.Contains(channelToSubscribe.Channel, "orders.") || - strings.Contains(channelToSubscribe.Channel, "accounts") { - return h.wsAuthenticatedSubscribe("sub", wsAccountsOrdersEndPoint+channelToSubscribe.Channel, channelToSubscribe.Channel) +func (h *HUOBI) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + if strings.Contains(channelsToSubscribe[i].Channel, "orders.") || + strings.Contains(channelsToSubscribe[i].Channel, "accounts") { + err := h.wsAuthenticatedSubscribe("sub", + wsAccountsOrdersEndPoint+channelsToSubscribe[i].Channel, + channelsToSubscribe[i].Channel) + if err != nil { + errs = append(errs, err) + continue + } + h.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) + continue + } + err := h.Websocket.Conn.SendJSONMessage(WsRequest{ + Subscribe: channelsToSubscribe[i].Channel, + }) + if err != nil { + errs = append(errs, err) + continue + } + h.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) } - return h.WebsocketConn.SendJSONMessage(WsRequest{Subscribe: channelToSubscribe.Channel}) + if errs != nil { + return errs + } + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (h *HUOBI) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - if strings.Contains(channelToSubscribe.Channel, "orders.") || - strings.Contains(channelToSubscribe.Channel, "accounts") { - return h.wsAuthenticatedSubscribe("unsub", wsAccountsOrdersEndPoint+channelToSubscribe.Channel, channelToSubscribe.Channel) +func (h *HUOBI) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToUnsubscribe { + if strings.Contains(channelsToUnsubscribe[i].Channel, "orders.") || + strings.Contains(channelsToUnsubscribe[i].Channel, "accounts") { + err := h.wsAuthenticatedSubscribe("unsub", + wsAccountsOrdersEndPoint+channelsToUnsubscribe[i].Channel, + channelsToUnsubscribe[i].Channel) + if err != nil { + errs = append(errs, err) + continue + } + h.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) + continue + } + err := h.Websocket.Conn.SendJSONMessage(WsRequest{ + Unsubscribe: channelsToUnsubscribe[i].Channel, + }) + if err != nil { + errs = append(errs, err) + continue + } + h.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) } - return h.WebsocketConn.SendJSONMessage(WsRequest{Unsubscribe: channelToSubscribe.Channel}) + if errs != nil { + return errs + } + return nil } func (h *HUOBI) wsGenerateSignature(timestamp, endpoint string) []byte { @@ -504,7 +587,7 @@ func (h *HUOBI) wsLogin() error { } hmac := h.wsGenerateSignature(timestamp, wsAccountsOrdersEndPoint) request.Signature = crypto.Base64Encode(hmac) - err := h.AuthenticatedWebsocketConn.SendJSONMessage(request) + err := h.Websocket.AuthConn.SendJSONMessage(request) if err != nil { h.Websocket.SetCanUseAuthenticatedEndpoints(false) return err @@ -526,7 +609,7 @@ func (h *HUOBI) wsAuthenticatedSubscribe(operation, endpoint, topic string) erro } hmac := h.wsGenerateSignature(timestamp, endpoint) request.Signature = crypto.Base64Encode(hmac) - return h.AuthenticatedWebsocketConn.SendJSONMessage(request) + return h.Websocket.AuthConn.SendJSONMessage(request) } func (h *HUOBI) wsGetAccountsList() (*WsAuthenticatedAccountsListResponse, error) { @@ -544,20 +627,34 @@ func (h *HUOBI) wsGetAccountsList() (*WsAuthenticatedAccountsListResponse, error } hmac := h.wsGenerateSignature(timestamp, wsAccountListEndpoint) request.Signature = crypto.Base64Encode(hmac) - request.ClientID = h.AuthenticatedWebsocketConn.GenerateMessageID(true) - resp, err := h.AuthenticatedWebsocketConn.SendMessageReturnResponse(request.ClientID, request) + request.ClientID = h.Websocket.AuthConn.GenerateMessageID(true) + resp, err := h.Websocket.AuthConn.SendMessageReturnResponse(request.ClientID, request) if err != nil { return nil, err } var response WsAuthenticatedAccountsListResponse err = json.Unmarshal(resp, &response) - return &response, err + if err != nil { + return nil, err + } + + code, _ := response.ErrorCode.(int) + if code != 0 { + return nil, errors.New(response.ErrorMessage) + } + return &response, nil } func (h *HUOBI) wsGetOrdersList(accountID int64, pair currency.Pair) (*WsAuthenticatedOrdersResponse, error) { if !h.Websocket.CanUseAuthenticatedEndpoints() { return nil, fmt.Errorf("%v not authenticated cannot get orders list", h.Name) } + + fpair, err := h.FormatExchangeCurrency(pair, asset.Spot) + if err != nil { + return nil, err + } + timestamp := time.Now().UTC().Format(wsDateTimeFormatting) request := WsAuthenticatedOrdersListRequest{ Op: requestOp, @@ -567,19 +664,30 @@ func (h *HUOBI) wsGetOrdersList(accountID int64, pair currency.Pair) (*WsAuthent Timestamp: timestamp, Topic: wsOrdersList, AccountID: accountID, - Symbol: h.FormatExchangeCurrency(pair, asset.Spot).String(), + Symbol: fpair.String(), States: "submitted,partial-filled", } + hmac := h.wsGenerateSignature(timestamp, wsOrdersListEndpoint) request.Signature = crypto.Base64Encode(hmac) - request.ClientID = h.AuthenticatedWebsocketConn.GenerateMessageID(true) - resp, err := h.AuthenticatedWebsocketConn.SendMessageReturnResponse(request.ClientID, request) + request.ClientID = h.Websocket.AuthConn.GenerateMessageID(true) + + resp, err := h.Websocket.AuthConn.SendMessageReturnResponse(request.ClientID, request) if err != nil { return nil, err } + var response WsAuthenticatedOrdersResponse err = json.Unmarshal(resp, &response) - return &response, err + if err != nil { + return nil, err + } + + code, _ := response.ErrorCode.(int) + if code != 0 { + return nil, errors.New(response.ErrorMessage) + } + return &response, nil } func (h *HUOBI) wsGetOrderDetails(orderID string) (*WsAuthenticatedOrderDetailResponse, error) { @@ -598,12 +706,20 @@ func (h *HUOBI) wsGetOrderDetails(orderID string) (*WsAuthenticatedOrderDetailRe } hmac := h.wsGenerateSignature(timestamp, wsOrdersDetailEndpoint) request.Signature = crypto.Base64Encode(hmac) - request.ClientID = h.AuthenticatedWebsocketConn.GenerateMessageID(true) - resp, err := h.AuthenticatedWebsocketConn.SendMessageReturnResponse(request.ClientID, request) + request.ClientID = h.Websocket.AuthConn.GenerateMessageID(true) + resp, err := h.Websocket.AuthConn.SendMessageReturnResponse(request.ClientID, request) if err != nil { return nil, err } var response WsAuthenticatedOrderDetailResponse err = json.Unmarshal(resp, &response) - return &response, err + if err != nil { + return nil, err + } + + code, _ := response.ErrorCode.(int) + if code != 0 { + return nil, errors.New(response.ErrorMessage) + } + return &response, nil } diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index 7d9f2889..65a1a825 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,19 +56,11 @@ func (h *HUOBI) SetDefaults() { h.API.CredentialsValidator.RequiresKey = true h.API.CredentialsValidator.RequiresSecret = true - h.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: false, - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "-", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{} + configFmt := ¤cy.PairFormat{Delimiter: currency.DashDelimiter, Uppercase: true} + err := h.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } h.Features = exchange.Features{ @@ -138,7 +130,7 @@ func (h *HUOBI) SetDefaults() { h.API.Endpoints.URLDefault = huobiAPIURL h.API.Endpoints.URL = h.API.Endpoints.URLDefault h.API.Endpoints.WebsocketURL = wsMarketURL - h.Websocket = wshandler.New() + h.Websocket = stream.New() h.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit h.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout h.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -156,51 +148,41 @@ func (h *HUOBI) Setup(exch *config.ExchangeConfig) error { return err } - err = h.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: wsMarketURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: h.WsConnect, - Subscriber: h.Subscribe, - UnSubscriber: h.Unsubscribe, - Features: &h.Features.Supports.WebsocketCapabilities, - }) + err = h.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: wsMarketURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: h.WsConnect, + Subscriber: h.Subscribe, + UnSubscriber: h.Unsubscribe, + GenerateSubscriptions: h.GenerateDefaultSubscriptions, + Features: &h.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + }) if err != nil { return err } - h.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: h.Name, - URL: wsMarketURL, - ProxyURL: h.Websocket.GetProxyAddress(), - Verbose: h.Verbose, - RateLimit: rateLimit, - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - h.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: h.Name, - URL: wsAccountsOrdersURL, - ProxyURL: h.Websocket.GetProxyAddress(), - Verbose: h.Verbose, + err = h.Websocket.SetupNewConnection(stream.ConnectionSetup{ RateLimit: rateLimit, ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + }) + if err != nil { + return err } - h.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - false, - false, - false, - exch.Name) - return nil + return h.Websocket.SetupNewConnection(stream.ConnectionSetup{ + RateLimit: rateLimit, + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + URL: wsAccountsOrdersURL, + Authenticated: true, + }) } // Start starts the HUOBI go routine @@ -224,14 +206,31 @@ func (h *HUOBI) Run() { } var forceUpdate bool - if common.StringDataContains(h.GetEnabledPairs(asset.Spot).Strings(), currency.CNY.String()) || - common.StringDataContains(h.GetAvailablePairs(asset.Spot).Strings(), currency.CNY.String()) { + enabled, err := h.GetEnabledPairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s Failed to update enabled currencies. Err:%s\n", + h.Name, + err) + } + + avail, err := h.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s Failed to update enabled currencies. Err:%s\n", + h.Name, + err) + } + + if common.StringDataContains(enabled.Strings(), currency.CNY.String()) || + common.StringDataContains(avail.Strings(), currency.CNY.String()) { forceUpdate = true } if common.StringDataContains(h.BaseCurrencies.Strings(), currency.CNY.String()) { cfg := config.GetConfig() - exchCfg, err := cfg.GetExchangeConfig(h.Name) + var exchCfg *config.ExchangeConfig + exchCfg, err = cfg.GetExchangeConfig(h.Name) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to get exchange config. %s\n", @@ -244,20 +243,31 @@ func (h *HUOBI) Run() { } if forceUpdate { - enabledPairs := currency.Pairs{currency.Pair{ - Base: currency.BTC.Lower(), - Quote: currency.USDT.Lower(), - Delimiter: h.GetPairFormat(asset.Spot, false).Delimiter, - }, + var format currency.PairFormat + format, err = h.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to get exchange config. %s\n", + h.Name, + err) + return + } + enabledPairs := currency.Pairs{ + currency.Pair{ + Base: currency.BTC.Lower(), + Quote: currency.USDT.Lower(), + Delimiter: format.Delimiter, + }, } log.Warn(log.ExchangeSys, "Available and enabled pairs for Huobi reset due to config upgrade, please enable the ones you would like again") - err := h.UpdatePairs(enabledPairs, asset.Spot, true, true) + err = h.UpdatePairs(enabledPairs, asset.Spot, true, true) if err != nil { log.Errorf(log.ExchangeSys, - "%s Failed to update enabled currencies.\n", - h.Name) + "%s Failed to update enabled currencies. Err:%s\n", + h.Name, + err) } } @@ -265,7 +275,7 @@ func (h *HUOBI) Run() { return } - err := h.UpdateTradablePairs(forceUpdate) + err = h.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", @@ -281,13 +291,18 @@ func (h *HUOBI) FetchTradablePairs(asset asset.Item) ([]string, error) { return nil, err } + format, err := h.GetPairFormat(asset, false) + if err != nil { + return nil, err + } + var pairs []string for x := range symbols { if symbols[x].State != "online" { continue } pairs = append(pairs, symbols[x].BaseCurrency+ - h.GetPairFormat(asset, false).Delimiter+ + format.Delimiter+ symbols[x].QuoteCurrency) } @@ -302,37 +317,45 @@ func (h *HUOBI) UpdateTradablePairs(forceUpdate bool) error { return err } - return h.UpdatePairs(currency.NewPairsFromStrings(pairs), - asset.Spot, - false, - forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return h.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (h *HUOBI) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) tickers, err := h.GetTickers() if err != nil { - return tickerPrice, err + return nil, err + } + pairs, err := h.GetEnabledPairs(assetType) + if err != nil { + return nil, err } - pairs := h.GetEnabledPairs(assetType) for i := range pairs { for j := range tickers.Data { - pairFmt := h.FormatExchangeCurrency(pairs[i], assetType).String() - if pairFmt != tickers.Data[j].Symbol { + pairFmt, err := h.FormatExchangeCurrency(pairs[i], assetType) + if err != nil { + return nil, err + } + + if pairFmt.String() != tickers.Data[j].Symbol { continue } - tickerPrice := &ticker.Price{ - High: tickers.Data[j].High, - Low: tickers.Data[j].Low, - Volume: tickers.Data[j].Volume, - Open: tickers.Data[j].Open, - Close: tickers.Data[j].Close, - Pair: pairs[i], - } - err = ticker.ProcessTicker(h.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + High: tickers.Data[j].High, + Low: tickers.Data[j].Low, + Volume: tickers.Data[j].Volume, + Open: tickers.Data[j].Open, + Close: tickers.Data[j].Close, + Pair: pairs[i], + ExchangeName: h.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } @@ -360,15 +383,19 @@ func (h *HUOBI) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderboo // UpdateOrderbook updates and returns the orderbook for a currency pair func (h *HUOBI) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) + fpair, err := h.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } orderbookNew, err := h.GetDepth(OrderBookDataRequestParams{ - Symbol: h.FormatExchangeCurrency(p, assetType).String(), + Symbol: fpair.String(), Type: OrderBookDataRequestParamsTypeStep0, }) if err != nil { - return orderBook, err + return nil, err } + orderBook := new(orderbook.Base) for x := range orderbookNew.Bids { orderBook.Bids = append(orderBook.Bids, orderbook.Item{ Amount: orderbookNew.Bids[x][1], @@ -533,11 +560,16 @@ func (h *HUOBI) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { return submitOrderResponse, err } + p, err := h.FormatExchangeCurrency(s.Pair, s.AssetType) + if err != nil { + return submitOrderResponse, err + } + var formattedType SpotNewOrderRequestParamsType var params = SpotNewOrderRequestParams{ Amount: s.Amount, Source: "api", - Symbol: h.FormatExchangeCurrency(s.Pair, s.AssetType).String(), + Symbol: p.String(), AccountID: int(accountID), } @@ -590,10 +622,18 @@ func (h *HUOBI) CancelOrder(order *order.Cancel) error { // CancelAllOrders cancels all orders associated with a currency pair func (h *HUOBI) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) { var cancelAllOrdersResponse order.CancelAllResponse - enabledPairs := h.GetEnabledPairs(asset.Spot) + enabledPairs, err := h.GetEnabledPairs(asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } for i := range enabledPairs { + fpair, err := h.FormatExchangeCurrency(enabledPairs[i], asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } + resp, err := h.CancelOpenOrdersBatch(orderCancellation.AccountID, - h.FormatExchangeCurrency(enabledPairs[i], asset.Spot).String()) + fpair.String()) if err != nil { return cancelAllOrdersResponse, err } @@ -683,7 +723,6 @@ func (h *HUOBI) GetOrderInfo(orderID string) (order.Detail, error) { if err != nil { return orderDetail, err } - orderDetail = order.Detail{ Exchange: h.Name, ID: orderID, @@ -732,11 +771,6 @@ func (h *HUOBI) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.R return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (h *HUOBI) GetWebsocket() (*wshandler.Websocket, error) { - return h.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (h *HUOBI) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !h.AllowAuthenticatedRequest() && // Todo check connection status @@ -814,8 +848,13 @@ func (h *HUOBI) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, er } } else { for i := range req.Pairs { + p, err := h.FormatExchangeCurrency(req.Pairs[i], asset.Spot) + if err != nil { + return nil, err + } + resp, err := h.GetOpenOrders(h.API.Credentials.ClientID, - h.FormatExchangeCurrency(req.Pairs[i], asset.Spot).String(), + p.String(), side, 500) if err != nil { @@ -857,8 +896,13 @@ func (h *HUOBI) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er states := "partial-canceled,filled,canceled" var orders []order.Detail for i := range req.Pairs { + p, err := h.FormatExchangeCurrency(req.Pairs[i], asset.Spot) + if err != nil { + return nil, err + } + resp, err := h.GetOrders( - h.FormatExchangeCurrency(req.Pairs[i], asset.Spot).String(), + p.String(), "", "", "", @@ -911,25 +955,6 @@ func setOrderSideAndType(requestType string, orderDetail *order.Detail) { } } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (h *HUOBI) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - h.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (h *HUOBI) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - h.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (h *HUOBI) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return h.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (h *HUOBI) AuthenticateWebsocket() error { return h.wsLogin() @@ -969,9 +994,14 @@ func (h *HUOBI) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end } } + formattedPair, err := h.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + klineParams := KlinesRequestParams{ Period: h.FormatExchangeKlineInterval(interval), - Symbol: h.FormatExchangeCurrency(pair, a).String(), + Symbol: formattedPair.String(), } candles, err := h.GetSpotKline(klineParams) diff --git a/exchanges/interfaces.go b/exchanges/interfaces.go index b0acce07..72afb5bb 100644 --- a/exchanges/interfaces.go +++ b/exchanges/interfaces.go @@ -11,8 +11,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -32,8 +32,8 @@ type IBotExchange interface { UpdateOrderbook(p currency.Pair, a asset.Item) (*orderbook.Base, error) FetchTradablePairs(a asset.Item) ([]string, error) UpdateTradablePairs(forceUpdate bool) error - GetEnabledPairs(a asset.Item) currency.Pairs - GetAvailablePairs(a asset.Item) currency.Pairs + GetEnabledPairs(a asset.Item) (currency.Pairs, error) + GetAvailablePairs(a asset.Item) (currency.Pairs, error) FetchAccountInfo() (account.Holdings, error) UpdateAccountInfo() (account.Holdings, error) GetAuthenticatedAPISupport(endpoint uint8) bool @@ -62,14 +62,8 @@ type IBotExchange interface { SetHTTPClientUserAgent(ua string) GetHTTPClientUserAgent() string SetClientProxyAddress(addr string) error - SupportsWebsocket() bool SupportsREST() bool - IsWebsocketEnabled() bool - GetWebsocket() (*wshandler.Websocket, error) - SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error - UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error - AuthenticateWebsocket() error - GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) + GetSubscriptions() ([]stream.ChannelSubscription, error) GetDefaultConfig() (*config.ExchangeConfig, error) GetBase() *Base SupportsAsset(assetType asset.Item) bool @@ -77,4 +71,16 @@ type IBotExchange interface { GetHistoricCandlesExtended(p currency.Pair, a asset.Item, timeStart, timeEnd time.Time, interval kline.Interval) (kline.Item, error) DisableRateLimiter() error EnableRateLimiter() error + + // Websocket specific wrapper functionality + // GetWebsocket returns a pointer to the websocket + GetWebsocket() (*stream.Websocket, error) + IsWebsocketEnabled() bool + SupportsWebsocket() bool + SubscribeToWebsocketChannels(channels []stream.ChannelSubscription) error + UnsubscribeToWebsocketChannels(channels []stream.ChannelSubscription) error + // FlushWebsocketChannels checks and flushes subscriptions if there is a + // pair,asset, url/proxy or subscription change + FlushWebsocketChannels() error + AuthenticateWebsocket() error } diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index 59a6871b..db157315 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -20,7 +20,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,17 +55,11 @@ func (i *ItBit) SetDefaults() { i.API.CredentialsValidator.RequiresClientID = true i.API.CredentialsValidator.RequiresSecret = true - i.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Uppercase: true} + configFmt := ¤cy.PairFormat{Uppercase: true} + err := i.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } i.Features = exchange.Features{ @@ -110,7 +103,6 @@ func (i *ItBit) Setup(exch *config.ExchangeConfig) error { i.SetEnabled(false) return nil } - return i.SetupDefaults(exch) } @@ -143,25 +135,30 @@ func (i *ItBit) UpdateTradablePairs(forceUpdate bool) error { // UpdateTicker updates and returns the ticker for a currency pair func (i *ItBit) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) - tick, err := i.GetTicker(i.FormatExchangeCurrency(p, assetType).String()) + fpair, err := i.FormatExchangeCurrency(p, assetType) if err != nil { - return tickerPrice, err + return nil, err } - tickerPrice = &ticker.Price{ - Last: tick.LastPrice, - High: tick.High24h, - Low: tick.Low24h, - Bid: tick.Bid, - Ask: tick.Ask, - Volume: tick.Volume24h, - Open: tick.OpenToday, - Pair: p, - LastUpdated: tick.ServertimeUTC, - } - err = ticker.ProcessTicker(i.Name, tickerPrice, assetType) + + tick, err := i.GetTicker(fpair.String()) if err != nil { - return tickerPrice, err + return nil, err + } + + err = ticker.ProcessTicker(&ticker.Price{ + Last: tick.LastPrice, + High: tick.High24h, + Low: tick.Low24h, + Bid: tick.Bid, + Ask: tick.Ask, + Volume: tick.Volume24h, + Open: tick.OpenToday, + Pair: p, + LastUpdated: tick.ServertimeUTC, + ExchangeName: i.Name, + AssetType: assetType}) + if err != nil { + return nil, err } return ticker.GetTicker(i.Name, p, assetType) @@ -187,12 +184,17 @@ func (i *ItBit) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderboo // UpdateOrderbook updates and returns the orderbook for a currency pair func (i *ItBit) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - orderbookNew, err := i.GetOrderbook(i.FormatExchangeCurrency(p, assetType).String()) + fpair, err := i.FormatExchangeCurrency(p, assetType) if err != nil { - return orderBook, err + return nil, err } + orderbookNew, err := i.GetOrderbook(fpair.String()) + if err != nil { + return nil, err + } + + orderBook := new(orderbook.Base) for x := range orderbookNew.Bids { var price, amount float64 price, err = strconv.ParseFloat(orderbookNew.Bids[x][0], 64) @@ -424,11 +426,6 @@ func (i *ItBit) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.R return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (i *ItBit) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrFunctionNotSupported -} - // GetFeeByType returns an estimate of fee based on type of transaction func (i *ItBit) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !i.AllowAuthenticatedRequest() && // Todo check connection status @@ -447,17 +444,27 @@ func (i *ItBit) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, er var allOrders []Order for x := range wallets { - resp, err := i.GetOrders(wallets[x].ID, "", "open", 0, 0) + var resp []Order + resp, err = i.GetOrders(wallets[x].ID, "", "open", 0, 0) if err != nil { return nil, err } allOrders = append(allOrders, resp...) } + format, err := i.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for j := range allOrders { - symbol := currency.NewPairDelimiter(allOrders[j].Instrument, - i.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err := currency.NewPairDelimiter(allOrders[j].Instrument, + format.Delimiter) + if err != nil { + return nil, err + } side := order.Side(strings.ToUpper(allOrders[j].Side)) orderDate, err := time.Parse(time.RFC3339, allOrders[j].CreatedTime) if err != nil { @@ -497,21 +504,31 @@ func (i *ItBit) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er var allOrders []Order for x := range wallets { - resp, err := i.GetOrders(wallets[x].ID, "", "", 0, 0) + var resp []Order + resp, err = i.GetOrders(wallets[x].ID, "", "", 0, 0) if err != nil { return nil, err } allOrders = append(allOrders, resp...) } + format, err := i.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for j := range allOrders { if allOrders[j].Type == "open" { continue } + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(allOrders[j].Instrument, + format.Delimiter) + if err != nil { + return nil, err + } - symbol := currency.NewPairDelimiter(allOrders[j].Instrument, - i.GetPairFormat(asset.Spot, false).Delimiter) side := order.Side(strings.ToUpper(allOrders[j].Side)) orderDate, err := time.Parse(time.RFC3339, allOrders[j].CreatedTime) if err != nil { @@ -541,28 +558,6 @@ func (i *ItBit) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (i *ItBit) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (i *ItBit) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (i *ItBit) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (i *ItBit) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (i *ItBit) ValidateCredentials() error { diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index 1398b141..af51ebb0 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -16,7 +16,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -64,9 +63,7 @@ var ( // Kraken is the overarching type across the alphapoint package type Kraken struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection - AuthenticatedWebsocketConn *wshandler.WebsocketConnection - wsRequestMtx sync.Mutex + wsRequestMtx sync.Mutex } // GetServerTime returns current server time diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index 02ef59f1..9ae3ca07 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -18,7 +18,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -48,12 +48,11 @@ func TestMain(m *testing.M) { krakenConfig.API.Credentials.Key = apiKey krakenConfig.API.Credentials.Secret = apiSecret krakenConfig.API.Endpoints.WebsocketURL = k.API.Endpoints.WebsocketURL + k.Websocket = sharedtestvalues.NewTestWebsocket() err = k.Setup(krakenConfig) if err != nil { log.Fatal(err) } - k.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - k.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -697,31 +696,14 @@ func setupWsTests(t *testing.T) { return } if !k.Websocket.IsEnabled() && !k.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) - } - k.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - comms = make(chan wshandler.WebsocketResponse, sharedtestvalues.WebsocketChannelOverrideCapacity) - k.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() - k.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: k.Name, - URL: krakenWSURL, - Verbose: k.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, - } - k.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: k.Name, - URL: krakenAuthWSURL, - Verbose: k.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + t.Skip(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := k.WebsocketConn.Dial(&dialer, http.Header{}) + err := k.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } - err = k.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{}) + err = k.Websocket.AuthConn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } @@ -731,10 +713,10 @@ func setupWsTests(t *testing.T) { t.Error(err) } authToken = token - - go k.wsFunnelConnectionData(k.WebsocketConn) - go k.wsFunnelConnectionData(k.AuthenticatedWebsocketConn) - go k.wsReadData() + comms := make(chan stream.Response) + go k.wsFunnelConnectionData(k.Websocket.Conn, comms) + go k.wsFunnelConnectionData(k.Websocket.AuthConn, comms) + go k.wsReadData(comms) go k.wsPingHandler() wsSetupRan = true } @@ -742,9 +724,11 @@ func setupWsTests(t *testing.T) { // TestWebsocketSubscribe tests returning a message with an id func TestWebsocketSubscribe(t *testing.T) { setupWsTests(t) - err := k.Subscribe(wshandler.WebsocketChannelSubscription{ - Channel: defaultSubscribedChannels[0], - Currency: currency.NewPairWithDelimiter("XBT", "USD", "/"), + err := k.Subscribe([]stream.ChannelSubscription{ + { + Channel: defaultSubscribedChannels[0], + Currency: currency.NewPairWithDelimiter("XBT", "USD", "/"), + }, }) if err != nil { t.Error(err) @@ -1426,8 +1410,11 @@ func TestParseTime(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("XBTUSD") - _, err := k.GetHistoricCandles(currencyPair, asset.Spot, time.Now().AddDate(0, 0, -1), time.Now(), kline.OneMin) + currencyPair, err := currency.NewPairFromString("XBTUSD") + if err != nil { + t.Fatal(err) + } + _, err = k.GetHistoricCandles(currencyPair, asset.Spot, time.Now().AddDate(0, 0, -1), time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } @@ -1439,8 +1426,11 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("XBTUSD") - _, err := k.GetHistoricCandlesExtended(currencyPair, asset.Spot, time.Now().AddDate(0, -6, 0), time.Now(), kline.OneDay) + currencyPair, err := currency.NewPairFromString("XBTUSD") + if err != nil { + t.Fatal(err) + } + _, err = k.GetHistoricCandlesExtended(currencyPair, asset.Spot, time.Now().AddDate(0, -6, 0), time.Now(), kline.OneDay) if err != nil { t.Fatal(err) } diff --git a/exchanges/kraken/kraken_types.go b/exchanges/kraken/kraken_types.go index 2077ce82..d9e7aafd 100644 --- a/exchanges/kraken/kraken_types.go +++ b/exchanges/kraken/kraken_types.go @@ -5,6 +5,7 @@ import ( "time" "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" ) type assetTranslatorStore struct { @@ -400,10 +401,11 @@ type WithdrawStatusResponse struct { // WebsocketSubscriptionEventRequest handles WS subscription events type WebsocketSubscriptionEventRequest struct { - Event string `json:"event"` // subscribe - RequestID int64 `json:"reqid,omitempty"` // Optional, client originated ID reflected in response message. - Pairs []string `json:"pair,omitempty"` // Array of currency pairs (pair1,pair2,pair3). - Subscription WebsocketSubscriptionData `json:"subscription,omitempty"` + Event string `json:"event"` // subscribe + RequestID int64 `json:"reqid,omitempty"` // Optional, client originated ID reflected in response message. + Pairs []string `json:"pair,omitempty"` // Array of currency pairs (pair1,pair2,pair3). + Subscription WebsocketSubscriptionData `json:"subscription,omitempty"` + Channels []stream.ChannelSubscription `json:"-"` // Keeps track of associated subscriptions in batched outgoings } // WebsocketBaseEventRequest Just has an "event" property diff --git a/exchanges/kraken/kraken_websocket.go b/exchanges/kraken/kraken_websocket.go index 4db9b84b..7031181b 100644 --- a/exchanges/kraken/kraken_websocket.go +++ b/exchanges/kraken/kraken_websocket.go @@ -10,15 +10,16 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -49,89 +50,110 @@ const ( krakenWsPingDelay = time.Second * 27 ) -// orderbookMutex Ensures if two entries arrive at once, only one can be processed at a time +// orderbookMutex Ensures if two entries arrive at once, only one can be +// processed at a time var subscriptionChannelPair []WebsocketChannelData -var comms = make(chan wshandler.WebsocketResponse) var authToken string -var pingRequest = WebsocketBaseEventRequest{Event: wshandler.Ping} +var pingRequest = WebsocketBaseEventRequest{Event: stream.Ping} // Channels require a topic and a currency // Format [[ticker,but-t4u],[orderbook,nce-btt]] -var defaultSubscribedChannels = []string{krakenWsTicker, krakenWsTrade, krakenWsOrderbook, krakenWsOHLC, krakenWsSpread} +var defaultSubscribedChannels = []string{krakenWsTicker, + krakenWsTrade, + krakenWsOrderbook, + krakenWsOHLC, + krakenWsSpread} var authenticatedChannels = []string{krakenWsOwnTrades, krakenWsOpenOrders} // WsConnect initiates a websocket connection func (k *Kraken) WsConnect() error { if !k.Websocket.IsEnabled() || !k.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } + var dialer websocket.Dialer - err := k.WebsocketConn.Dial(&dialer, http.Header{}) + err := k.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } + + comms := make(chan stream.Response) + go k.wsReadData(comms) + go k.wsFunnelConnectionData(k.Websocket.Conn, comms) + if k.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { authToken, err = k.GetWebsocketToken() if err != nil { k.Websocket.SetCanUseAuthenticatedEndpoints(false) - log.Errorf(log.ExchangeSys, "%v - authentication failed: %v\n", k.Name, err) + log.Errorf(log.ExchangeSys, + "%v - authentication failed: %v\n", + k.Name, + err) + } else { + err = k.Websocket.AuthConn.Dial(&dialer, http.Header{}) + if err != nil { + k.Websocket.SetCanUseAuthenticatedEndpoints(false) + log.Errorf(log.ExchangeSys, + "%v - failed to connect to authenticated endpoint: %v\n", + k.Name, + err) + } else { + go k.wsFunnelConnectionData(k.Websocket.AuthConn, comms) + var authsubs []stream.ChannelSubscription + authsubs, err = k.GenerateAuthenticatedSubscriptions() + if err != nil { + return err + } + err = k.Websocket.SubscribeToChannels(authsubs) + if err != nil { + return err + } + } } - err = k.AuthenticatedWebsocketConn.Dial(&dialer, http.Header{}) - if err != nil { - k.Websocket.SetCanUseAuthenticatedEndpoints(false) - log.Errorf(log.ExchangeSys, "%v - failed to connect to authenticated endpoint: %v\n", k.Name, err) - } - go k.wsFunnelConnectionData(k.AuthenticatedWebsocketConn) - k.GenerateAuthenticatedSubscriptions() } - go k.wsFunnelConnectionData(k.WebsocketConn) - go k.wsReadData() err = k.wsPingHandler() if err != nil { - log.Errorf(log.ExchangeSys, "%v - failed setup ping handler. Websocket may disconnect unexpectedly. %v\n", k.Name, err) + log.Errorf(log.ExchangeSys, + "%v - failed setup ping handler. Websocket may disconnect unexpectedly. %v\n", + k.Name, + err) } - k.GenerateDefaultSubscriptions() - - return nil + gensubs, err := k.GenerateDefaultSubscriptions() + if err != nil { + return err + } + return k.Websocket.SubscribeToChannels(gensubs) } // wsFunnelConnectionData funnels both auth and public ws data into one manageable place -func (k *Kraken) wsFunnelConnectionData(ws *wshandler.WebsocketConnection) { +func (k *Kraken) wsFunnelConnectionData(ws stream.Connection, comms chan stream.Response) { k.Websocket.Wg.Add(1) defer k.Websocket.Wg.Done() for { - select { - case <-k.Websocket.ShutdownC: + resp := ws.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := ws.ReadMessage() - if err != nil { - k.Websocket.DataHandler <- err - return - } - k.Websocket.TrafficAlert <- struct{}{} - comms <- resp } + comms <- resp } } // wsReadData receives and passes on websocket messages for processing -func (k *Kraken) wsReadData() { +func (k *Kraken) wsReadData(comms chan stream.Response) { k.Websocket.Wg.Add(1) - defer func() { - k.Websocket.Wg.Done() - }() + defer k.Websocket.Wg.Done() for { select { case <-k.Websocket.ShutdownC: return - default: - resp := <-comms + case resp := <-comms: err := k.wsHandleData(resp.Raw) if err != nil { - k.Websocket.DataHandler <- fmt.Errorf("%s - unhandled websocket data: %v", k.Name, err) + k.Websocket.DataHandler <- fmt.Errorf("%s - unhandled websocket data: %v", + k.Name, + err) } } } @@ -160,34 +182,49 @@ func (k *Kraken) wsHandleData(respRaw []byte) error { var eventResponse map[string]interface{} err := json.Unmarshal(respRaw, &eventResponse) if err != nil { - return fmt.Errorf("%s - err %s could not parse websocket data: %s", k.Name, err, respRaw) + return fmt.Errorf("%s - err %s could not parse websocket data: %s", + k.Name, + err, + respRaw) } if event, ok := eventResponse["event"]; ok { switch event { - case wshandler.Pong, krakenWsHeartbeat, krakenWsCancelOrderStatus: + case stream.Pong, krakenWsHeartbeat, krakenWsCancelOrderStatus: return nil case krakenWsSystemStatus: var systemStatus wsSystemStatus err := json.Unmarshal(respRaw, &systemStatus) if err != nil { - return fmt.Errorf("%s - err %s unable to parse system status response: %s", k.Name, err, respRaw) + return fmt.Errorf("%s - err %s unable to parse system status response: %s", + k.Name, + err, + respRaw) } if systemStatus.Status != "online" { k.Websocket.DataHandler <- fmt.Errorf("%v Websocket status '%v'", - k.Name, systemStatus.Status) + k.Name, + systemStatus.Status) } if systemStatus.Version > krakenWSSupportedVersion { - log.Warnf(log.ExchangeSys, "%v New version of Websocket API released. Was %v Now %v", - k.Name, krakenWSSupportedVersion, systemStatus.Version) + log.Warnf(log.ExchangeSys, + "%v New version of Websocket API released. Was %v Now %v", + k.Name, + krakenWSSupportedVersion, + systemStatus.Version) } case krakenWsAddOrderStatus: var status WsAddOrderResponse err := json.Unmarshal(respRaw, &status) if err != nil { - return fmt.Errorf("%s - err %s unable to parse add order response: %s", k.Name, err, respRaw) + return fmt.Errorf("%s - err %s unable to parse add order response: %s", + k.Name, + err, + respRaw) } if status.ErrorMessage != "" { - return fmt.Errorf("%s - err %s", k.Name, status.ErrorMessage) + return fmt.Errorf("%s - err %s", + k.Name, + status.ErrorMessage) } k.Websocket.DataHandler <- &order.Detail{ Exchange: k.Name, @@ -198,20 +235,27 @@ func (k *Kraken) wsHandleData(respRaw []byte) error { var sub wsSubscription err := json.Unmarshal(respRaw, &sub) if err != nil { - return fmt.Errorf("%s - err %s unable to parse subscription response: %s", k.Name, err, respRaw) + return fmt.Errorf("%s - err %s unable to parse subscription response: %s", + k.Name, + err, + respRaw) } if sub.Status != "subscribed" && sub.Status != "unsubscribed" { - return fmt.Errorf("%v %v %v", k.Name, sub.RequestID, sub.ErrorMessage) + return fmt.Errorf("%v %v %v", + k.Name, + sub.RequestID, + sub.ErrorMessage) } k.addNewSubscriptionChannelData(&sub) if sub.RequestID > 0 { - if k.WebsocketConn.IsIDWaitingForResponse(sub.RequestID) { - k.WebsocketConn.SetResponseIDAndData(sub.RequestID, respRaw) + if k.Websocket.Match.IncomingWithData(sub.RequestID, respRaw) { return nil } } default: - k.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: k.Name + wshandler.UnhandledMessage + string(respRaw)} + k.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: k.Name + stream.UnhandledMessage + string(respRaw), + } } return nil } @@ -225,7 +269,7 @@ func (k *Kraken) wsPingHandler() error { if err != nil { return err } - k.WebsocketConn.SetupPingHandler(wshandler.WebsocketPingHandler{ + k.Websocket.Conn.SetupPingHandler(stream.PingHandler{ Message: message, Delay: krakenWsPingDelay, MessageType: websocket.TextMessage, @@ -366,7 +410,16 @@ func (k *Kraken) wsProcessOpenOrders(ownOrders interface{}) error { Err: err, } } - p := currency.NewPairFromString(val.Description.Pair) + + p, err := currency.NewPairFromString(val.Description.Pair) + if err != nil { + k.Websocket.DataHandler <- order.ClassificationError{ + Exchange: k.Name, + OrderID: key, + Err: err, + } + } + var a asset.Item a, err = k.GetPairAssetType(p) if err != nil { @@ -409,7 +462,12 @@ func (k *Kraken) addNewSubscriptionChannelData(response *wsSubscription) { // We change the / to - to maintain compatibility with REST/config var pair currency.Pair if response.Pair != "" { - pair = currency.NewPairFromString(response.Pair) + var err error + pair, err = currency.NewPairFromString(response.Pair) + if err != nil { + log.Errorf(log.ExchangeSys, "%s exchange error: %s", k.Name, err) + return + } pair.Delimiter = k.CurrencyPairs.RequestFormat.Delimiter } subscriptionChannelPair = append(subscriptionChannelPair, WebsocketChannelData{ @@ -525,7 +583,13 @@ func (k *Kraken) wsProcessTrades(channelData *WebsocketChannelData, data []inter if trade[3].(string) == "s" { tSide = order.Sell } - k.Websocket.DataHandler <- wshandler.TradeData{ + + var tType = order.Market + if trade[4].(string) == "l" { + tType = order.Limit + } + + k.Websocket.DataHandler <- stream.TradeData{ AssetType: asset.Spot, CurrencyPair: channelData.Pair, Exchange: k.Name, @@ -533,6 +597,7 @@ func (k *Kraken) wsProcessTrades(channelData *WebsocketChannelData, data []inter Amount: amount, Timestamp: convert.TimeFromUnixTimestampDecimal(timeData), Side: tSide, + EventType: tType, } } } @@ -554,9 +619,10 @@ func (k *Kraken) wsProcessOrderBook(channelData *WebsocketChannelData, data map[ defer k.wsRequestMtx.Unlock() err := k.wsProcessOrderBookUpdate(channelData, askData, bidData) if err != nil { - subscriptionToRemove := wshandler.WebsocketChannelSubscription{ + subscriptionToRemove := &stream.ChannelSubscription{ Channel: krakenWsOrderbook, Currency: channelData.Pair, + Asset: asset.Spot, } k.Websocket.ResubscribeToChannel(subscriptionToRemove) return err @@ -625,21 +691,12 @@ func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, as } base.LastUpdated = highestLastUpdate base.ExchangeName = k.Name - err := k.Websocket.Orderbook.LoadSnapshot(&base) - if err != nil { - return err - } - k.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: k.Name, - Asset: asset.Spot, - Pair: channelData.Pair, - } - return nil + return k.Websocket.Orderbook.LoadSnapshot(&base) } // wsProcessOrderBookUpdate updates an orderbook entry for a given currency pair func (k *Kraken) wsProcessOrderBookUpdate(channelData *WebsocketChannelData, askData, bidData []interface{}) error { - update := wsorderbook.WebsocketOrderbookUpdate{ + update := buffer.Update{ Asset: asset.Spot, Pair: channelData.Pair, } @@ -701,17 +758,7 @@ func (k *Kraken) wsProcessOrderBookUpdate(channelData *WebsocketChannelData, ask } } update.UpdateTime = highestLastUpdate - err := k.Websocket.Orderbook.Update(&update) - if err != nil { - k.Websocket.DataHandler <- err - return err - } - k.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: k.Name, - Asset: asset.Spot, - Pair: channelData.Pair, - } - return nil + return k.Websocket.Orderbook.Update(&update) } // wsProcessCandles converts candle data and sends it to the data handler @@ -751,7 +798,7 @@ func (k *Kraken) wsProcessCandles(channelData *WebsocketChannelData, data []inte return err } - k.Websocket.DataHandler <- wshandler.KlineData{ + k.Websocket.DataHandler <- stream.KlineData{ AssetType: asset.Spot, Pair: channelData.Pair, Timestamp: time.Now(), @@ -770,78 +817,177 @@ func (k *Kraken) wsProcessCandles(channelData *WebsocketChannelData, data []inte } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (k *Kraken) GenerateDefaultSubscriptions() { - enabledCurrencies := k.GetEnabledPairs(asset.Spot) - var subscriptions []wshandler.WebsocketChannelSubscription +func (k *Kraken) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + enabledCurrencies, err := k.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + var subscriptions []stream.ChannelSubscription for i := range defaultSubscribedChannels { for j := range enabledCurrencies { enabledCurrencies[j].Delimiter = "/" - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: defaultSubscribedChannels[i], Currency: enabledCurrencies[j], + Asset: asset.Spot, }) } } - k.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // GenerateAuthenticatedSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (k *Kraken) GenerateAuthenticatedSubscriptions() { - var subscriptions []wshandler.WebsocketChannelSubscription +func (k *Kraken) GenerateAuthenticatedSubscriptions() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription for i := range authenticatedChannels { params := make(map[string]interface{}) - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: authenticatedChannels[i], Params: params, }) } - k.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (k *Kraken) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - resp := WebsocketSubscriptionEventRequest{ - Event: krakenWsSubscribe, - Subscription: WebsocketSubscriptionData{ - Name: channelToSubscribe.Channel, - }, - RequestID: k.WebsocketConn.GenerateMessageID(false), - } - if channelToSubscribe.Channel == "book" { - // TODO: Add ability to make depth customisable - resp.Subscription.Depth = 1000 - } - if !channelToSubscribe.Currency.IsEmpty() { - resp.Pairs = []string{channelToSubscribe.Currency.String()} - } - if channelToSubscribe.Params != nil { - resp.Subscription.Token = authToken +func (k *Kraken) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var subs []WebsocketSubscriptionEventRequest +channels: + for x := range channelsToSubscribe { + for y := range subs { + if subs[y].Subscription.Name == channelsToSubscribe[x].Channel { + subs[y].Pairs = append(subs[y].Pairs, + channelsToSubscribe[x].Currency.String()) + subs[y].Channels = append(subs[y].Channels, channelsToSubscribe[x]) + continue channels + } + } + + var id int64 + if common.StringDataContains(authenticatedChannels, channelsToSubscribe[x].Channel) { + id = k.Websocket.AuthConn.GenerateMessageID(false) + } else { + id = k.Websocket.Conn.GenerateMessageID(false) + } + + resp := WebsocketSubscriptionEventRequest{ + Event: krakenWsSubscribe, + Subscription: WebsocketSubscriptionData{ + Name: channelsToSubscribe[x].Channel, + }, + RequestID: id, + } + if channelsToSubscribe[x].Channel == "book" { + // TODO: Add ability to make depth customisable + resp.Subscription.Depth = 1000 + } + if !channelsToSubscribe[x].Currency.IsEmpty() { + resp.Pairs = []string{channelsToSubscribe[x].Currency.String()} + } + if channelsToSubscribe[x].Params != nil { + resp.Subscription.Token = authToken + } + + resp.Channels = append(resp.Channels, channelsToSubscribe[x]) + subs = append(subs, resp) } - _, err := k.WebsocketConn.SendMessageReturnResponse(resp.RequestID, resp) - return err + var errs common.Errors + for i := range subs { + if common.StringDataContains(authenticatedChannels, subs[i].Subscription.Name) { + _, err := k.Websocket.AuthConn.SendMessageReturnResponse(subs[i].RequestID, subs[i]) + if err != nil { + errs = append(errs, err) + continue + } + k.Websocket.AddSuccessfulSubscriptions(subs[i].Channels...) + continue + } + + _, err := k.Websocket.Conn.SendMessageReturnResponse(subs[i].RequestID, subs[i]) + if err != nil { + errs = append(errs, err) + continue + } + k.Websocket.AddSuccessfulSubscriptions(subs[i].Channels...) + } + if errs != nil { + return errs + } + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (k *Kraken) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - resp := WebsocketSubscriptionEventRequest{ - Event: krakenWsUnsubscribe, - Pairs: []string{channelToSubscribe.Currency.String()}, - Subscription: WebsocketSubscriptionData{ - Name: channelToSubscribe.Channel, - }, - RequestID: k.WebsocketConn.GenerateMessageID(false), +func (k *Kraken) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var unsubs []WebsocketSubscriptionEventRequest +channels: + for x := range channelsToUnsubscribe { + for y := range unsubs { + if unsubs[y].Subscription.Name == channelsToUnsubscribe[x].Channel { + unsubs[y].Pairs = append(unsubs[y].Pairs, + channelsToUnsubscribe[x].Currency.String()) + unsubs[y].Channels = append(unsubs[y].Channels, + channelsToUnsubscribe[x]) + continue channels + } + } + var depth int64 + if channelsToUnsubscribe[x].Channel == "book" { + // TODO: Add ability to make depth customisable + depth = 1000 + } + + var id int64 + if common.StringDataContains(authenticatedChannels, channelsToUnsubscribe[x].Channel) { + id = k.Websocket.AuthConn.GenerateMessageID(false) + } else { + id = k.Websocket.Conn.GenerateMessageID(false) + } + + unsub := WebsocketSubscriptionEventRequest{ + Event: krakenWsUnsubscribe, + Pairs: []string{channelsToUnsubscribe[x].Currency.String()}, + Subscription: WebsocketSubscriptionData{ + Name: channelsToUnsubscribe[x].Channel, + Depth: depth, + }, + RequestID: id, + } + unsub.Channels = append(unsub.Channels, channelsToUnsubscribe[x]) + unsubs = append(unsubs, unsub) } - _, err := k.WebsocketConn.SendMessageReturnResponse(resp.RequestID, resp) - return err + + var errs common.Errors + for i := range unsubs { + if common.StringDataContains(authenticatedChannels, unsubs[i].Subscription.Name) { + _, err := k.Websocket.AuthConn.SendMessageReturnResponse(unsubs[i].RequestID, unsubs[i]) + if err != nil { + errs = append(errs, err) + continue + } + k.Websocket.RemoveSuccessfulUnsubscriptions(unsubs[i].Channels...) + continue + } + + _, err := k.Websocket.Conn.SendMessageReturnResponse(unsubs[i].RequestID, unsubs[i]) + if err != nil { + errs = append(errs, err) + continue + } + k.Websocket.RemoveSuccessfulUnsubscriptions(unsubs[i].Channels...) + } + if errs != nil { + return errs + } + return nil } func (k *Kraken) wsAddOrder(request *WsAddOrderRequest) (string, error) { - id := k.AuthenticatedWebsocketConn.GenerateMessageID(false) + id := k.Websocket.AuthConn.GenerateMessageID(false) request.UserReferenceID = strconv.FormatInt(id, 10) request.Event = krakenWsAddOrder request.Token = authToken - jsonResp, err := k.AuthenticatedWebsocketConn.SendMessageReturnResponse(id, request) + jsonResp, err := k.Websocket.AuthConn.SendMessageReturnResponse(id, request) if err != nil { return "", err } @@ -862,5 +1008,5 @@ func (k *Kraken) wsCancelOrders(orderIDs []string) error { Token: authToken, TransactionIDs: orderIDs, } - return k.AuthenticatedWebsocketConn.SendJSONMessage(request) + return k.Websocket.AuthConn.SendJSONMessage(request) } diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 1e39c9e4..c55af9fc 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -57,21 +57,18 @@ func (k *Kraken) SetDefaults() { k.API.CredentialsValidator.RequiresSecret = true k.API.CredentialsValidator.RequiresBase64DecodeSecret = true - k.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - Separator: ",", - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "-", - Separator: ",", - }, + requestFmt := ¤cy.PairFormat{ + Uppercase: true, + Separator: ",", + } + configFmt := ¤cy.PairFormat{ + Uppercase: true, + Delimiter: currency.DashDelimiter, + Separator: ",", + } + err := k.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } k.Features = exchange.Features{ @@ -149,7 +146,7 @@ func (k *Kraken) SetDefaults() { k.API.Endpoints.URLDefault = krakenAPIURL k.API.Endpoints.URL = k.API.Endpoints.URLDefault - k.Websocket = wshandler.New() + k.Websocket = stream.New() k.API.Endpoints.WebsocketURL = krakenWSURL k.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit k.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout @@ -173,52 +170,44 @@ func (k *Kraken) Setup(exch *config.ExchangeConfig) error { return err } - err = k.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: krakenWSURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: k.WsConnect, - Subscriber: k.Subscribe, - UnSubscriber: k.Unsubscribe, - Features: &k.Features.Supports.WebsocketCapabilities, - }) + err = k.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: krakenWSURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: k.WsConnect, + Subscriber: k.Subscribe, + UnSubscriber: k.Unsubscribe, + GenerateSubscriptions: k.GenerateDefaultSubscriptions, + Features: &k.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + BufferEnabled: true, + SortBuffer: true, + }) if err != nil { return err } - k.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: k.Name, - URL: k.Websocket.GetWebsocketURL(), - ProxyURL: k.Websocket.GetProxyAddress(), - Verbose: k.Verbose, + err = k.Websocket.SetupNewConnection(stream.ConnectionSetup{ RateLimit: krakenWsRateLimit, ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, + URL: krakenWSURL, + }) + if err != nil { + return err } - k.AuthenticatedWebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: k.Name, + return k.Websocket.SetupNewConnection(stream.ConnectionSetup{ + RateLimit: krakenWsRateLimit, + ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, + ResponseMaxLimit: exch.WebsocketResponseMaxLimit, URL: krakenAuthWSURL, - ProxyURL: k.Websocket.GetProxyAddress(), - Verbose: k.Verbose, - RateLimit: krakenWsRateLimit, - ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, - ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - k.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - true, - true, - false, - false, - exch.Name) - return nil + Authenticated: true, + }) } // Start starts the Kraken go routine @@ -237,22 +226,55 @@ func (k *Kraken) Run() { } forceUpdate := false - delim := k.GetPairFormat(asset.Spot, false).Delimiter - if !common.StringDataContains(k.GetEnabledPairs(asset.Spot).Strings(), delim) || - !common.StringDataContains(k.GetAvailablePairs(asset.Spot).Strings(), delim) || - common.StringDataContains(k.GetAvailablePairs(asset.Spot).Strings(), "ZUSD") { - enabledPairs := currency.NewPairsFromStrings( - []string{currency.XBT.String() + delim + currency.USD.String()}, - ) - log.Warn(log.ExchangeSys, "Available pairs for Kraken reset due to config upgrade, please enable the ones you would like again") - forceUpdate = true + format, err := k.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + k.Name, + err) + return + } + enabled, err := k.GetEnabledPairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + k.Name, + err) + return + } - err := k.UpdatePairs(enabledPairs, asset.Spot, true, true) + avail, err := k.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + k.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) || + common.StringDataContains(avail.Strings(), "ZUSD") { + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.XBT.String() + + format.Delimiter + + currency.USD.String()}) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies. Err: %s\n", k.Name, err) + } else { + log.Warn(log.ExchangeSys, "Available pairs for Kraken reset due to config upgrade, please enable the ones you would like again") + forceUpdate = true + + err = k.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + k.Name, + err) + } } } @@ -260,7 +282,7 @@ func (k *Kraken) Run() { return } - err := k.UpdateTradablePairs(forceUpdate) + err = k.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", @@ -282,6 +304,11 @@ func (k *Kraken) FetchTradablePairs(asset asset.Item) ([]string, error) { return nil, err } + format, err := k.GetPairFormat(asset, false) + if err != nil { + return nil, err + } + var products []string for i := range pairs { if strings.Contains(pairs[i].Altname, ".d") { @@ -305,8 +332,7 @@ func (k *Kraken) FetchTradablePairs(asset asset.Item) ([]string, error) { pairs[i].Quote) continue } - products = append(products, - base+k.GetPairFormat(asset, false).Delimiter+quote) + products = append(products, base+format.Delimiter+quote) } return products, nil } @@ -319,48 +345,57 @@ func (k *Kraken) UpdateTradablePairs(forceUpdate bool) error { return err } - return k.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return k.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (k *Kraken) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) - pairs := k.GetEnabledPairs(assetType) + pairs, err := k.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } pairsCollated, err := k.FormatExchangeCurrencies(pairs, assetType) if err != nil { - return tickerPrice, err + return nil, err } tickers, err := k.GetTickers(pairsCollated) if err != nil { - return tickerPrice, err + return nil, err } for i := range pairs { for c, t := range tickers { - pairFmt := k.FormatExchangeCurrency(pairs[i], assetType).String() - if !strings.EqualFold(pairFmt, c) { + pairFmt, err := k.FormatExchangeCurrency(pairs[i], assetType) + if err != nil { + return nil, err + } + if !strings.EqualFold(pairFmt.String(), c) { altCurrency := assetTranslator.LookupAltname(c) if altCurrency == "" { continue } - if !strings.EqualFold(pairFmt, altCurrency) { - continue + if !strings.EqualFold(pairFmt.String(), altCurrency) { + continue // This looks dodge } } - tickerPrice = &ticker.Price{ - Last: t.Last, - High: t.High, - Low: t.Low, - Bid: t.Bid, - Ask: t.Ask, - Volume: t.Volume, - Open: t.Open, - Pair: pairs[i], - } - err = ticker.ProcessTicker(k.Name, tickerPrice, assetType) + err = ticker.ProcessTicker(&ticker.Price{ + Last: t.Last, + High: t.High, + Low: t.Low, + Bid: t.Bid, + Ask: t.Ask, + Volume: t.Volume, + Open: t.Open, + Pair: pairs[i], + ExchangeName: k.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } @@ -387,19 +422,29 @@ func (k *Kraken) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbo // UpdateOrderbook updates and returns the orderbook for a currency pair func (k *Kraken) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { - orderBook := new(orderbook.Base) - orderbookNew, err := k.GetDepth(k.FormatExchangeCurrency(p, - assetType).String()) + fpair, err := k.FormatExchangeCurrency(p, assetType) if err != nil { - return orderBook, err + return nil, err } + orderbookNew, err := k.GetDepth(fpair.String()) + if err != nil { + return nil, err + } + + var orderBook = new(orderbook.Base) for x := range orderbookNew.Bids { - orderBook.Bids = append(orderBook.Bids, orderbook.Item{Amount: orderbookNew.Bids[x].Amount, Price: orderbookNew.Bids[x].Price}) + orderBook.Bids = append(orderBook.Bids, orderbook.Item{ + Amount: orderbookNew.Bids[x].Amount, + Price: orderbookNew.Bids[x].Price, + }) } for x := range orderbookNew.Asks { - orderBook.Asks = append(orderBook.Asks, orderbook.Item{Amount: orderbookNew.Asks[x].Amount, Price: orderbookNew.Asks[x].Price}) + orderBook.Asks = append(orderBook.Asks, orderbook.Item{ + Amount: orderbookNew.Asks[x].Amount, + Price: orderbookNew.Asks[x].Price, + }) } orderBook.Pair = p @@ -569,6 +614,16 @@ func (k *Kraken) GetOrderInfo(orderID string) (order.Detail, error) { return orderDetail, err } if orderInfo, ok := openOrders.Open[orderID]; ok { + avail, err := k.GetAvailablePairs(asset.Spot) + if err != nil { + return orderDetail, err + } + + fmt, err := k.GetPairFormat(asset.Spot, false) + if err != nil { + return orderDetail, err + } + var trades []order.TradeHistory for i := range orderInfo.Trades { trades = append(trades, order.TradeHistory{ @@ -588,11 +643,16 @@ func (k *Kraken) GetOrderInfo(orderID string) (order.Detail, error) { return orderDetail, err } + p, err := currency.NewPairFromFormattedPairs(orderInfo.Description.Pair, + avail, + fmt) + if err != nil { + return orderDetail, err + } orderDetail = order.Detail{ - Exchange: k.Name, - ID: orderID, - Pair: currency.NewPairFromFormattedPairs(orderInfo.Description.Pair, - k.GetAvailablePairs(asset.Spot), k.GetPairFormat(asset.Spot, true)), + Exchange: k.Name, + ID: orderID, + Pair: p, Side: side, Type: oType, Date: convert.TimeFromUnixTimestampDecimal(orderInfo.OpenTime), @@ -666,11 +726,6 @@ func (k *Kraken) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw. }, nil } -// GetWebsocket returns a pointer to the exchange websocket -func (k *Kraken) GetWebsocket() (*wshandler.Websocket, error) { - return k.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (k *Kraken) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !k.AllowAuthenticatedRequest() && // Todo check connection status @@ -687,8 +742,25 @@ func (k *Kraken) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e return nil, err } + avail, err := k.GetAvailablePairs(asset.Spot) + if err != nil { + return nil, err + } + + fmt, err := k.GetPairFormat(asset.Spot, true) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range resp.Open { + p, err := currency.NewPairFromFormattedPairs(resp.Open[i].Description.Pair, + avail, + fmt) + if err != nil { + return nil, err + } + side := order.Side(strings.ToUpper(resp.Open[i].Description.Type)) orderType := order.Type(strings.ToUpper(resp.Open[i].Description.OrderType)) orders = append(orders, order.Detail{ @@ -701,8 +773,7 @@ func (k *Kraken) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, e Price: resp.Open[i].Description.Price, Side: side, Type: orderType, - Pair: currency.NewPairFromFormattedPairs(resp.Open[i].Description.Pair, - k.GetAvailablePairs(asset.Spot), k.GetPairFormat(asset.Spot, true)), + Pair: p, }) } @@ -723,6 +794,16 @@ func (k *Kraken) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]or req.End = strconv.FormatInt(getOrdersRequest.EndTicks.Unix(), 10) } + avail, err := k.GetAvailablePairs(asset.Spot) + if err != nil { + return nil, err + } + + fmt, err := k.GetPairFormat(asset.Spot, true) + if err != nil { + return nil, err + } + resp, err := k.GetClosedOrders(req) if err != nil { return nil, err @@ -730,6 +811,13 @@ func (k *Kraken) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]or var orders []order.Detail for i := range resp.Closed { + p, err := currency.NewPairFromFormattedPairs(resp.Closed[i].Description.Pair, + avail, + fmt) + if err != nil { + return nil, err + } + side := order.Side(strings.ToUpper(resp.Closed[i].Description.Type)) orderType := order.Type(strings.ToUpper(resp.Closed[i].Description.OrderType)) orders = append(orders, order.Detail{ @@ -743,8 +831,7 @@ func (k *Kraken) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]or Price: resp.Closed[i].Description.Price, Side: side, Type: orderType, - Pair: currency.NewPairFromFormattedPairs(resp.Closed[i].Description.Pair, - k.GetAvailablePairs(asset.Spot), k.GetPairFormat(asset.Spot, true)), + Pair: p, }) } @@ -753,25 +840,6 @@ func (k *Kraken) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]or return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (k *Kraken) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - k.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (k *Kraken) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - k.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (k *Kraken) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return k.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (k *Kraken) AuthenticateWebsocket() error { resp, err := k.GetWebsocketToken() @@ -807,7 +875,13 @@ func (k *Kraken) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end Asset: a, Interval: interval, } - candles, err := k.GetOHLC(assetTranslator.LookupCurrency(k.FormatExchangeCurrency(pair, a).Upper().String()), k.FormatExchangeKlineInterval(interval)) + + formattedPair, err := k.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + + candles, err := k.GetOHLC(assetTranslator.LookupCurrency(formattedPair.Upper().String()), k.FormatExchangeKlineInterval(interval)) if err != nil { return kline.Item{}, err } @@ -848,7 +922,12 @@ func (k *Kraken) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, st Interval: interval, } - candles, err := k.GetOHLC(assetTranslator.LookupCurrency(k.FormatExchangeCurrency(pair, a).Upper().String()), k.FormatExchangeKlineInterval(interval)) + formattedPair, err := k.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + + candles, err := k.GetOHLC(assetTranslator.LookupCurrency(formattedPair.Upper().String()), k.FormatExchangeKlineInterval(interval)) if err != nil { return kline.Item{}, err } diff --git a/exchanges/lakebtc/lakebtc_test.go b/exchanges/lakebtc/lakebtc_test.go index 460f35b9..d1d38320 100644 --- a/exchanges/lakebtc/lakebtc_test.go +++ b/exchanges/lakebtc/lakebtc_test.go @@ -14,7 +14,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -36,18 +36,17 @@ func TestMain(m *testing.M) { } lakebtcConfig, err := cfg.GetExchangeConfig("LakeBTC") if err != nil { - log.Fatal("LakeBTC Setup() init error") + log.Fatal("LakeBTC Setup() init error", err) } lakebtcConfig.API.AuthenticatedSupport = true lakebtcConfig.API.Credentials.Key = apiKey lakebtcConfig.API.Credentials.Secret = apiSecret lakebtcConfig.Features.Enabled.Websocket = true + l.Websocket = sharedtestvalues.NewTestWebsocket() err = l.Setup(lakebtcConfig) if err != nil { log.Fatal("LakeBTC setup error", err) } - l.API.Endpoints.WebsocketURL = lakeBTCWSURL - os.Exit(m.Run()) } @@ -441,10 +440,8 @@ func TestGetDepositAddress(t *testing.T) { // TestWsConn websocket connection test func TestWsConn(t *testing.T) { if !l.Websocket.IsEnabled() { - t.Skip(wshandler.WebsocketNotEnabled) + t.Skip(stream.WebsocketNotEnabled) } - l.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - l.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() err := l.WsConnect() if err != nil { t.Fatal(err) @@ -453,8 +450,6 @@ func TestWsConn(t *testing.T) { // TestWsTradeProcessing logic test func TestWsTradeProcessing(t *testing.T) { - l.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - l.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() json := `{"trades":[{"type":"sell","date":1564985787,"price":"11913.02","amount":"0.49"}]}` err := l.processTrades(json, "market-btcusd-global") if err != nil { @@ -464,9 +459,6 @@ func TestWsTradeProcessing(t *testing.T) { // TestWsTickerProcessing logic test func TestWsTickerProcessing(t *testing.T) { - const testChanSize = 26 - l.Websocket.DataHandler = make(chan interface{}, testChanSize) - l.Websocket.TrafficAlert = make(chan struct{}, testChanSize) json := `{"btcusd":{"low":"10990.05","high":"11966.24","last":"11903.29","volume":"1803.967079","sell":"11912.39","buy":"11902.2"},"btceur":{"low":"9886.87","high":"10732.72","last":"10691.44","volume":"87.994478","sell":"10711.62","buy":"10691.44"},"btchkd":{"low":null,"high":null,"last":"51776.98","volume":null,"sell":"93307.37","buy":"93177.56"},"btcjpy":{"low":"1176039.0","high":"1272246.0","last":"1265680.0","volume":"129.021421","sell":"1266764.0","buy":"1265680.0"},"btcgbp":{"low":"9157.12","high":"9953.43","last":"9941.28","volume":"10.4997","sell":"10007.89","buy":"9941.28"},"btcaud":{"low":"16102.57","high":"17594.22","last":"17548.16","volume":"7.338316","sell":"17616.67","buy":"17549.69"},"btccad":{"low":"14541.69","high":"15834.87","last":"15763.54","volume":"30.480309","sell":"15793.45","buy":"15756.13"},"btcsgd":{"low":"15133.82","high":"16501.62","last":"16455.53","volume":"4.044026","sell":"16484.37","buy":"16462.18"},"btcchf":{"low":"10800.58","high":"11526.24","last":"11526.24","volume":"0.1765","sell":"11675.34","buy":"11632.02"},"btcnzd":{"low":null,"high":null,"last":"8340.98","volume":null,"sell":"18315.49","buy":"18221.37"},"btcngn":{"low":null,"high":null,"last":"600000.0","volume":null,"sell":null,"buy":null},"eurusd":{"low":"1.1088","high":"1.1138","last":"1.1125","volume":"2680.105249","sell":"1.1142","buy":"1.1121"},"gbpusd":{"low":"1.1934","high":"1.1958","last":"1.1934","volume":"1493.923823","sell":"1.1979","buy":"1.1903"},"usdjpy":{"low":"105.26","high":"107.25","last":"106.33","volume":"114490.2179","sell":"106.34","buy":"106.27"},"usdhkd":{"low":null,"high":null,"last":"7.851","volume":null,"sell":"7.8328","buy":"7.8286"},"usdcad":{"low":"1.3225","high":"1.3272","last":"1.3255","volume":"11033.9877","sell":"1.3258","buy":"1.3238"},"usdsgd":{"low":"1.3776","high":"1.3839","last":"1.3838","volume":"2523.75","sell":"1.3838","buy":"1.3819"},"audusd":{"low":"0.6764","high":"0.6853","last":"0.6771","volume":"5442.608321","sell":"0.6782","buy":"0.6762"},"nzdusd":{"low":null,"high":null,"last":"0.6758","volume":null,"sell":"0.6532","buy":"0.6504"},"usdchf":{"low":"0.9838","high":"0.9838","last":"0.9838","volume":"108.3352","sell":"0.9801","buy":"0.9773"},"usdngn":{"low":null,"high":null,"last":"200.0","volume":null,"sell":null,"buy":null},"ethbtc":{"low":"0.0205","high":"0.025","last":"0.0205","volume":null,"sell":"0.03","buy":"0.0194"},"ltcbtc":{"low":null,"high":null,"last":"0.0114","volume":null,"sell":"0.009","buy":"0.0073"},"bchbtc":{"low":null,"high":null,"last":"0.0544","volume":null,"sell":"0.0322","buy":"0.0274"},"xrpbtc":{"low":"0.000042","high":"0.000042","last":"0.000042","volume":null,"sell":"0.000037","buy":"0.000022"},"baceth":{"low":"0.000035","high":"0.000035","last":"0.000035","volume":null,"sell":"0.0015","buy":null}}` err := l.processTicker(json) if err != nil { @@ -476,16 +468,20 @@ func TestWsTickerProcessing(t *testing.T) { func TestGetCurrencyFromChannel(t *testing.T) { curr := currency.NewPair(currency.LTC, currency.BTC) - result := l.getCurrencyFromChannel(marketSubstring + curr.String() + globalSubstring) - if curr != result { + result, err := l.getCurrencyFromChannel(marketSubstring + + curr.String() + + globalSubstring) + if err != nil { + t.Fatal(err) + } + + if !curr.Equal(result) { t.Errorf("currency result is not equal. Expected %v", curr) } } // TestWsOrderbookProcessing logic test func TestWsOrderbookProcessing(t *testing.T) { - l.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - l.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() json := `{"asks":[["11905.66","0.0019"],["11905.73","0.0015"],["11906.43","0.0013"],["11906.62","0.0019"],["11907.25","11.087"],["11907.66","0.0006"],["11907.73","0.3113"],["11907.84","0.0006"],["11908.37","0.0016"],["11908.86","10.3786"],["11909.54","4.2955"],["11910.15","0.0012"],["11910.56","13.5505"],["11911.06","0.0011"],["11911.37","0.0023"]],"bids":[["11905.55","0.0171"],["11904.43","0.0225"],["11903.31","0.0223"],["11902.2","0.0027"],["11901.92","1.002"],["11901.6","0.0015"],["11901.49","0.0012"],["11901.08","0.0227"],["11900.93","0.0009"],["11900.53","1.662"],["11900.08","0.001"],["11900.01","3.6745"],["11899.96","0.003"],["11899.91","0.0006"],["11899.44","0.0013"]]}` err := l.processOrderbook(json, "market-btcusd-global") if err != nil { diff --git a/exchanges/lakebtc/lakebtc_types.go b/exchanges/lakebtc/lakebtc_types.go index f0949b85..4dde4310 100644 --- a/exchanges/lakebtc/lakebtc_types.go +++ b/exchanges/lakebtc/lakebtc_types.go @@ -125,8 +125,8 @@ type WebsocketConn struct { // WsOrderbookUpdate contains orderbook data from websocket type WsOrderbookUpdate struct { - Asks [][]string `json:"asks"` - Bids [][]string `json:"bids"` + Asks [][2]string `json:"asks"` + Bids [][2]string `json:"bids"` } // WsTrades contains trade data from websocket @@ -141,3 +141,12 @@ type WsTrade struct { Price float64 `json:"price,string"` Amount float64 `json:"amount,string"` } + +type wsTicker struct { + Low float64 `json:"low,string"` + High float64 `json:"high,string"` + Last float64 `json:"last,string"` + Volume float64 `json:"volume,string"` + Sell float64 `json:"sell,string"` + Buy float64 `json:"buy,string"` +} diff --git a/exchanges/lakebtc/lakebtc_websocket.go b/exchanges/lakebtc/lakebtc_websocket.go index 7af6622f..f197ba79 100644 --- a/exchanges/lakebtc/lakebtc_websocket.go +++ b/exchanges/lakebtc/lakebtc_websocket.go @@ -8,18 +8,19 @@ import ( "strings" "time" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/toorop/go-pusher" ) const ( - lakeBTCWSURL = "ws.lakebtc.com:8085" + lakeBTCWSURL = "wss://ws.lakebtc.com:8085" marketGlobalEndpoint = "market-global" marketSubstring = "market-" globalSubstring = "-global" @@ -33,10 +34,14 @@ const ( // WsConnect initiates a new websocket connection func (l *LakeBTC) WsConnect() error { if !l.Websocket.IsEnabled() || !l.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } + + url := strings.Split(lakeBTCWSURL, "://") var err error - l.WebsocketConn.Client, err = pusher.NewCustomClient(strings.ToLower(l.Name), lakeBTCWSURL, wssSchem) + l.WebsocketConn.Client, err = pusher.NewCustomClient(strings.ToLower(l.Name), + url[1], + wssSchem) if err != nil { return err } @@ -44,13 +49,17 @@ func (l *LakeBTC) WsConnect() error { if err != nil { return err } - l.GenerateDefaultSubscriptions() + err = l.listenToEndpoints() if err != nil { return err } go l.wsHandleIncomingData() - return nil + subs, err := l.GenerateDefaultSubscriptions() + if err != nil { + return err + } + return l.Websocket.SubscribeToChannels(subs) } func (l *LakeBTC) listenToEndpoints() error { @@ -71,9 +80,12 @@ func (l *LakeBTC) listenToEndpoints() error { } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (l *LakeBTC) GenerateDefaultSubscriptions() { - var subscriptions []wshandler.WebsocketChannelSubscription - enabledCurrencies := l.GetEnabledPairs(asset.Spot) +func (l *LakeBTC) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription + enabledCurrencies, err := l.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } for j := range enabledCurrencies { enabledCurrencies[j].Delimiter = "" @@ -81,23 +93,54 @@ func (l *LakeBTC) GenerateDefaultSubscriptions() { enabledCurrencies[j].Lower().String() + globalSubstring - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: channel, Currency: enabledCurrencies[j], + Asset: asset.Spot, }) } - l.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (l *LakeBTC) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - return l.WebsocketConn.Client.Subscribe(channelToSubscribe.Channel) +func (l *LakeBTC) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + err := l.WebsocketConn.Client.Subscribe(channelsToSubscribe[i].Channel) + if err != nil { + errs = append(errs, err) + continue + } + l.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) + } + if errs != nil { + return errs + } + return nil +} + +// Unsubscribe sends a websocket message to unsubscribe from the channel +func (l *LakeBTC) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToUnsubscribe { + err := l.WebsocketConn.Client.Unsubscribe(channelsToUnsubscribe[i].Channel) + if err != nil { + errs = append(errs, err) + continue + } + l.Websocket.RemoveSuccessfulUnsubscriptions(channelsToUnsubscribe[i]) + } + if errs != nil { + return errs + } + return nil } // wsHandleIncomingData services incoming data from the websocket connection func (l *LakeBTC) wsHandleIncomingData() { l.Websocket.Wg.Add(1) defer l.Websocket.Wg.Done() + for { select { case <-l.Websocket.ShutdownC: @@ -107,14 +150,11 @@ func (l *LakeBTC) wsHandleIncomingData() { log.Debugf(log.ExchangeSys, "%v Websocket message received: %v", l.Name, data) } - l.Websocket.TrafficAlert <- struct{}{} err := l.processTicker(data.Data) if err != nil { l.Websocket.DataHandler <- err - return } case data := <-l.WebsocketConn.Trade: - l.Websocket.TrafficAlert <- struct{}{} if l.Verbose { log.Debugf(log.ExchangeSys, "%v Websocket message received: %v", l.Name, data) @@ -122,10 +162,8 @@ func (l *LakeBTC) wsHandleIncomingData() { err := l.processTrades(data.Data, data.Channel) if err != nil { l.Websocket.DataHandler <- err - return } case data := <-l.WebsocketConn.Orderbook: - l.Websocket.TrafficAlert <- struct{}{} if l.Verbose { log.Debugf(log.ExchangeSys, "%v Websocket message received: %v", l.Name, data) @@ -133,9 +171,12 @@ func (l *LakeBTC) wsHandleIncomingData() { err := l.processOrderbook(data.Data, data.Channel) if err != nil { l.Websocket.DataHandler <- err - return } } + select { + case l.Websocket.TrafficAlert <- struct{}{}: + default: + } } } @@ -145,7 +186,11 @@ func (l *LakeBTC) processTrades(data, channel string) error { if err != nil { return err } - curr := l.getCurrencyFromChannel(channel) + curr, err := l.getCurrencyFromChannel(channel) + if err != nil { + return err + } + for i := range tradeData.Trades { tSide, err := order.StringToOrderSide(tradeData.Trades[i].Type) if err != nil { @@ -154,7 +199,7 @@ func (l *LakeBTC) processTrades(data, channel string) error { Err: err, } } - l.Websocket.DataHandler <- wshandler.TradeData{ + l.Websocket.DataHandler <- stream.TradeData{ Timestamp: time.Unix(tradeData.Trades[i].Date, 0), CurrencyPair: curr, AssetType: asset.Spot, @@ -175,7 +220,10 @@ func (l *LakeBTC) processOrderbook(obUpdate, channel string) error { return err } - p := l.getCurrencyFromChannel(channel) + p, err := l.getCurrencyFromChannel(channel) + if err != nil { + return err + } book := orderbook.Base{ Pair: p, @@ -188,13 +236,11 @@ func (l *LakeBTC) processOrderbook(obUpdate, channel string) error { var amount, price float64 amount, err = strconv.ParseFloat(update.Asks[i][1], 64) if err != nil { - l.Websocket.DataHandler <- fmt.Errorf("%v error parsing ticker data 'low' %v", l.Name, update.Asks[i]) - continue + return err } price, err = strconv.ParseFloat(update.Asks[i][0], 64) if err != nil { - l.Websocket.DataHandler <- fmt.Errorf("%v error parsing orderbook price %v", l.Name, update.Asks[i]) - continue + return err } book.Asks = append(book.Asks, orderbook.Item{ Amount: amount, @@ -206,13 +252,11 @@ func (l *LakeBTC) processOrderbook(obUpdate, channel string) error { var amount, price float64 amount, err = strconv.ParseFloat(update.Bids[i][1], 64) if err != nil { - l.Websocket.DataHandler <- fmt.Errorf("%v error parsing ticker data 'low' %v", l.Name, update.Bids[i]) - continue + return err } price, err = strconv.ParseFloat(update.Bids[i][0], 64) if err != nil { - l.Websocket.DataHandler <- fmt.Errorf("%v error parsing orderbook price %v", l.Name, update.Bids[i]) - continue + return err } book.Bids = append(book.Bids, orderbook.Item{ Amount: amount, @@ -220,67 +264,46 @@ func (l *LakeBTC) processOrderbook(obUpdate, channel string) error { }) } - err = l.Websocket.Orderbook.LoadSnapshot(&book) - if err != nil { - return err - } - - l.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: p, - Asset: asset.Spot, - Exchange: l.Name, - } - - return nil + return l.Websocket.Orderbook.LoadSnapshot(&book) } -func (l *LakeBTC) getCurrencyFromChannel(channel string) currency.Pair { +func (l *LakeBTC) getCurrencyFromChannel(channel string) (currency.Pair, error) { curr := strings.Replace(channel, marketSubstring, "", 1) curr = strings.Replace(curr, globalSubstring, "", 1) return currency.NewPairFromString(curr) } -func (l *LakeBTC) processTicker(wsTicker string) error { - var tUpdate map[string]interface{} - err := json.Unmarshal([]byte(wsTicker), &tUpdate) +func (l *LakeBTC) processTicker(data string) error { + var tUpdate map[string]wsTicker + err := json.Unmarshal([]byte(data), &tUpdate) if err != nil { l.Websocket.DataHandler <- err return err } - enabled := l.GetEnabledPairs(asset.Spot) + enabled, err := l.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + for k, v := range tUpdate { - returnCurrency := currency.NewPairFromString(k) + returnCurrency, err := currency.NewPairFromString(k) + if err != nil { + return err + } + if !enabled.Contains(returnCurrency, true) { continue } - tickerData := v.(map[string]interface{}) - processTickerItem := func(tick map[string]interface{}, item string) float64 { - if tick[item] == nil { - return 0 - } - - p, err := strconv.ParseFloat(tick[item].(string), 64) - if err != nil { - l.Websocket.DataHandler <- fmt.Errorf("%s error parsing ticker data '%s' %v", - l.Name, - item, - tickerData) - return 0 - } - - return p - } - l.Websocket.DataHandler <- &ticker.Price{ ExchangeName: l.Name, - Bid: processTickerItem(tickerData, order.Buy.Lower()), - High: processTickerItem(tickerData, tickerHighString), - Last: processTickerItem(tickerData, tickerLastString), - Low: processTickerItem(tickerData, tickerLowString), - Ask: processTickerItem(tickerData, order.Sell.Lower()), - Volume: processTickerItem(tickerData, tickerVolumeString), + Bid: v.Buy, + High: v.High, + Last: v.Last, + Low: v.Low, + Ask: v.Sell, + Volume: v.Volume, AssetType: asset.Spot, Pair: returnCurrency, } diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index ec0007c7..1b4aee13 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,18 +56,11 @@ func (l *LakeBTC) SetDefaults() { l.API.CredentialsValidator.RequiresKey = true l.API.CredentialsValidator.RequiresSecret = true - l.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Uppercase: true} + configFmt := ¤cy.PairFormat{Uppercase: true} + err := l.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } l.Features = exchange.Features{ @@ -95,6 +88,7 @@ func (l *LakeBTC) SetDefaults() { TradeFetching: true, OrderbookFetching: true, Subscribe: true, + Unsubscribe: true, }, WithdrawPermissions: exchange.AutoWithdrawCrypto | exchange.WithdrawFiatViaWebsiteOnly, @@ -109,7 +103,7 @@ func (l *LakeBTC) SetDefaults() { l.API.Endpoints.URLDefault = lakeBTCAPIURL l.API.Endpoints.URL = l.API.Endpoints.URLDefault - l.Websocket = wshandler.New() + l.Websocket = stream.New() l.API.Endpoints.WebsocketURL = lakeBTCWSURL l.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit l.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout @@ -128,31 +122,21 @@ func (l *LakeBTC) Setup(exch *config.ExchangeConfig) error { return err } - err = l.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: lakeBTCWSURL, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: l.WsConnect, - Subscriber: l.Subscribe, - Features: &l.Features.Supports.WebsocketCapabilities, - }) - if err != nil { - return err - } - - l.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - false, - false, - false, - exch.Name) - return nil + return l.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: lakeBTCWSURL, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: l.WsConnect, + Subscriber: l.Subscribe, + UnSubscriber: l.Unsubscribe, + GenerateSubscriptions: l.GenerateDefaultSubscriptions, + Features: &l.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + }) } // Start starts the LakeBTC go routine @@ -203,7 +187,11 @@ func (l *LakeBTC) UpdateTradablePairs(forceUpdate bool) error { return err } - return l.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return l.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair @@ -213,9 +201,18 @@ func (l *LakeBTC) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.P return nil, err } - pairs := l.GetEnabledPairs(assetType) + pairs, err := l.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } + for i := range pairs { - c, ok := ticks[l.FormatExchangeCurrency(pairs[i], assetType).String()] + fpair, err := l.FormatExchangeCurrency(pairs[i], assetType) + if err != nil { + return nil, err + } + + c, ok := ticks[fpair.String()] if !ok { continue } @@ -228,10 +225,12 @@ func (l *LakeBTC) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.P tickerPrice.High = c.High tickerPrice.Low = c.Low tickerPrice.Last = c.Last + tickerPrice.ExchangeName = l.Name + tickerPrice.AssetType = assetType - err = ticker.ProcessTicker(l.Name, tickerPrice, assetType) + err = ticker.ProcessTicker(tickerPrice) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } return ticker.GetTicker(l.Name, p, assetType) @@ -447,11 +446,6 @@ func (l *LakeBTC) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (l *LakeBTC) GetWebsocket() (*wshandler.Websocket, error) { - return l.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (l *LakeBTC) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !l.AllowAuthenticatedRequest() && // Todo check connection status @@ -468,10 +462,19 @@ func (l *LakeBTC) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, return nil, err } + format, err := l.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range resp { - symbol := currency.NewPairDelimiter(resp[i].Symbol, - l.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(resp[i].Symbol, + format.Delimiter) + if err != nil { + return nil, err + } orderDate := time.Unix(resp[i].At, 0) side := order.Side(strings.ToUpper(resp[i].Type)) @@ -501,14 +504,21 @@ func (l *LakeBTC) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return nil, err } + format, err := l.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range resp { if resp[i].State == "active" { continue } - - symbol := currency.NewPairDelimiter(resp[i].Symbol, - l.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(resp[i].Symbol, format.Delimiter) + if err != nil { + return nil, err + } orderDate := time.Unix(resp[i].At, 0) side := order.Side(strings.ToUpper(resp[i].Type)) @@ -530,28 +540,6 @@ func (l *LakeBTC) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (l *LakeBTC) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (l *LakeBTC) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (l *LakeBTC) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (l *LakeBTC) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (l *LakeBTC) ValidateCredentials() error { diff --git a/exchanges/lbank/lbank.go b/exchanges/lbank/lbank.go index c7c5ac45..b1d1c995 100644 --- a/exchanges/lbank/lbank.go +++ b/exchanges/lbank/lbank.go @@ -20,14 +20,14 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" ) // Lbank is the overarching type across this package type Lbank struct { exchange.Base privateKey *rsa.PrivateKey - WebsocketConn *wshandler.WebsocketConnection + WebsocketConn *stream.WebsocketConnection } const ( diff --git a/exchanges/lbank/lbank_test.go b/exchanges/lbank/lbank_test.go index 2772c056..47c6a4e2 100644 --- a/exchanges/lbank/lbank_test.go +++ b/exchanges/lbank/lbank_test.go @@ -410,8 +410,11 @@ func TestGetOrderHistory(t *testing.T) { func TestGetHistoricCandles(t *testing.T) { t.Parallel() - pair := currency.NewPairFromString(testCurrencyPair) - _, err := l.GetHistoricCandles(pair, asset.Spot, time.Now().Add(-24*time.Hour), time.Now(), kline.OneMin) + pair, err := currency.NewPairFromString(testCurrencyPair) + if err != nil { + t.Fatal(err) + } + _, err = l.GetHistoricCandles(pair, asset.Spot, time.Now().Add(-24*time.Hour), time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } @@ -427,8 +430,11 @@ func TestGetHistoricCandlesExtended(t *testing.T) { startTime := time.Now().Add(-time.Hour) end := time.Now() - pair := currency.NewPairFromString(testCurrencyPair) - _, err := l.GetHistoricCandlesExtended(pair, asset.Spot, startTime, end, kline.OneMin) + pair, err := currency.NewPairFromString(testCurrencyPair) + if err != nil { + t.Fatal(err) + } + _, err = l.GetHistoricCandlesExtended(pair, asset.Spot, startTime, end, kline.OneMin) if err != nil { t.Fatal(err) } diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go index 4653a872..53dea512 100644 --- a/exchanges/lbank/lbank_wrapper.go +++ b/exchanges/lbank/lbank_wrapper.go @@ -19,7 +19,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -55,18 +54,11 @@ func (l *Lbank) SetDefaults() { l.API.CredentialsValidator.RequiresKey = true l.API.CredentialsValidator.RequiresSecret = true - l.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "_", - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "_", - }, + requestFmt := ¤cy.PairFormat{Delimiter: currency.UnderscoreDelimiter} + configFmt := ¤cy.PairFormat{Delimiter: currency.UnderscoreDelimiter} + err := l.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } l.Features = exchange.Features{ @@ -184,33 +176,40 @@ func (l *Lbank) UpdateTradablePairs(forceUpdate bool) error { return err } - return l.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return l.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (l *Lbank) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) tickerInfo, err := l.GetTickers() if err != nil { - return tickerPrice, err + return nil, err + } + pairs, err := l.GetEnabledPairs(assetType) + if err != nil { + return nil, err } - pairs := l.GetEnabledPairs(assetType) for i := range pairs { for j := range tickerInfo { if !pairs[i].Equal(tickerInfo[j].Symbol) { continue } - tickerPrice = &ticker.Price{ - Last: tickerInfo[j].Ticker.Latest, - High: tickerInfo[j].Ticker.High, - Low: tickerInfo[j].Ticker.Low, - Volume: tickerInfo[j].Ticker.Volume, - Pair: tickerInfo[j].Symbol, - LastUpdated: time.Unix(0, tickerInfo[j].Timestamp), - } - err = ticker.ProcessTicker(l.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: tickerInfo[j].Ticker.Latest, + High: tickerInfo[j].Ticker.High, + Low: tickerInfo[j].Ticker.Low, + Volume: tickerInfo[j].Ticker.Volume, + Pair: tickerInfo[j].Symbol, + LastUpdated: time.Unix(0, tickerInfo[j].Timestamp), + ExchangeName: l.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } @@ -219,8 +218,12 @@ func (l *Lbank) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Pri // FetchTicker returns the ticker for a currency pair func (l *Lbank) FetchTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerNew, err := ticker.GetTicker(l.Name, - l.FormatExchangeCurrency(p, assetType), assetType) + fpair, err := l.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + + tickerNew, err := ticker.GetTicker(l.Name, fpair, assetType) if err != nil { return l.UpdateTicker(p, assetType) } @@ -239,7 +242,11 @@ func (l *Lbank) FetchOrderbook(currency currency.Pair, assetType asset.Item) (*o // UpdateOrderbook updates and returns the orderbook for a currency pair func (l *Lbank) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { orderBook := new(orderbook.Base) - a, err := l.GetMarketDepths(l.FormatExchangeCurrency(p, assetType).String(), "60", "1") + fpair, err := l.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + a, err := l.GetMarketDepths(fpair.String(), "60", "1") if err != nil { return orderBook, err } @@ -336,8 +343,14 @@ func (l *Lbank) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { fmt.Errorf("%s order side is not supported by the exchange", s.Side) } + + fpair, err := l.FormatExchangeCurrency(s.Pair, asset.Spot) + if err != nil { + return resp, err + } + tempResp, err := l.CreateOrder( - l.FormatExchangeCurrency(s.Pair, asset.Spot).String(), + fpair.String(), s.Side.String(), s.Amount, s.Price) @@ -360,8 +373,11 @@ func (l *Lbank) ModifyOrder(action *order.Modify) (string, error) { // CancelOrder cancels an order by its corresponding ID number func (l *Lbank) CancelOrder(order *order.Cancel) error { - _, err := l.RemoveOrder(l.FormatExchangeCurrency(order.Pair, - order.AssetType).String(), order.ID) + fpair, err := l.FormatExchangeCurrency(order.Pair, order.AssetType) + if err != nil { + return err + } + _, err = l.RemoveOrder(fpair.String(), order.ID) return err } @@ -440,7 +456,11 @@ func (l *Lbank) GetOrderInfo(orderID string) (order.Detail, error) { return resp, err } resp.Exchange = l.Name - resp.Pair = currency.NewPairFromString(key) + resp.Pair, err = currency.NewPairFromString(key) + if err != nil { + return order.Detail{}, err + } + if strings.EqualFold(tempResp.Orders[0].Type, order.Buy.String()) { resp.Side = order.Buy } else { @@ -508,11 +528,6 @@ func (l *Lbank) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.R return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (l *Lbank) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrNotYetImplemented -} - // GetActiveOrders retrieves any orders that are active/open func (l *Lbank) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) { var finalResp []order.Detail @@ -529,7 +544,11 @@ func (l *Lbank) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]ord return finalResp, err } resp.Exchange = l.Name - resp.Pair = currency.NewPairFromString(key) + resp.Pair, err = currency.NewPairFromString(key) + if err != nil { + return nil, err + } + if strings.EqualFold(tempResp.Orders[0].Type, order.Buy.String()) { resp.Side = order.Buy } else { @@ -587,25 +606,37 @@ func (l *Lbank) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]ord var resp order.Detail var tempCurr currency.Pairs if len(getOrdersRequest.Pairs) == 0 { - tempCurr = l.GetEnabledPairs(asset.Spot) + var err error + tempCurr, err = l.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } } else { tempCurr = getOrdersRequest.Pairs } for a := range tempCurr { - p := l.FormatExchangeCurrency(tempCurr[a], asset.Spot).String() + fpair, err := l.FormatExchangeCurrency(tempCurr[a], asset.Spot) + if err != nil { + return nil, err + } + b := int64(1) - tempResp, err := l.QueryOrderHistory(p, strconv.FormatInt(b, 10), "200") + tempResp, err := l.QueryOrderHistory(fpair.String(), strconv.FormatInt(b, 10), "200") if err != nil { return finalResp, err } for len(tempResp.Orders) != 0 { - tempResp, err = l.QueryOrderHistory(p, strconv.FormatInt(b, 10), "200") + tempResp, err = l.QueryOrderHistory(fpair.String(), strconv.FormatInt(b, 10), "200") if err != nil { return finalResp, err } for x := 0; x < len(tempResp.Orders); x++ { resp.Exchange = l.Name - resp.Pair = currency.NewPairFromString(tempResp.Orders[x].Symbol) + resp.Pair, err = currency.NewPairFromString(tempResp.Orders[x].Symbol) + if err != nil { + return nil, err + } + if strings.EqualFold(tempResp.Orders[x].Type, order.Buy.String()) { resp.Side = order.Buy } else { @@ -674,18 +705,28 @@ func (l *Lbank) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { // GetAllOpenOrderID returns all open orders by currency pairs func (l *Lbank) getAllOpenOrderID() (map[string][]string, error) { - allPairs := l.GetEnabledPairs(asset.Spot) + allPairs, err := l.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } resp := make(map[string][]string) for a := range allPairs { - p := l.FormatExchangeCurrency(allPairs[a], asset.Spot).String() + fpair, err := l.FormatExchangeCurrency(allPairs[a], asset.Spot) + if err != nil { + return nil, err + } b := int64(1) - tempResp, err := l.GetOpenOrders(p, strconv.FormatInt(b, 10), "200") + tempResp, err := l.GetOpenOrders(fpair.String(), + strconv.FormatInt(b, 10), + "200") if err != nil { return resp, err } tempData := len(tempResp.Orders) for tempData != 0 { - tempResp, err = l.GetOpenOrders(p, strconv.FormatInt(b, 10), "200") + tempResp, err = l.GetOpenOrders(fpair.String(), + strconv.FormatInt(b, 10), + "200") if err != nil { return resp, err } @@ -695,7 +736,8 @@ func (l *Lbank) getAllOpenOrderID() (map[string][]string, error) { } for c := 0; c < tempData; c++ { - resp[p] = append(resp[p], tempResp.Orders[c].OrderID) + resp[fpair.String()] = append(resp[fpair.String()], + tempResp.Orders[c].OrderID) } tempData = len(tempResp.Orders) b++ @@ -704,28 +746,6 @@ func (l *Lbank) getAllOpenOrderID() (map[string][]string, error) { return resp, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (l *Lbank) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrNotYetImplemented -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (l *Lbank) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrNotYetImplemented -} - -// AuthenticateWebsocket authenticates it -func (l *Lbank) AuthenticateWebsocket() error { - return common.ErrNotYetImplemented -} - -// GetSubscriptions gets subscriptions -func (l *Lbank) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrNotYetImplemented -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (l *Lbank) ValidateCredentials() error { @@ -758,7 +778,12 @@ func (l *Lbank) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end } } - data, err := l.GetKlines(l.FormatExchangeCurrency(pair, a).String(), + formattedPair, err := l.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + + data, err := l.GetKlines(formattedPair.String(), strconv.FormatInt(int64(l.Features.Enabled.Kline.ResultLimit), 10), l.FormatExchangeKlineInterval(interval), strconv.FormatInt(start.Unix(), 10)) @@ -803,8 +828,13 @@ func (l *Lbank) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, sta } dates := kline.CalcDateRanges(start, end, interval, l.Features.Enabled.Kline.ResultLimit) + formattedPair, err := l.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + for x := range dates { - data, err := l.GetKlines(l.FormatExchangeCurrency(pair, a).String(), + data, err := l.GetKlines(formattedPair.String(), strconv.FormatInt(int64(l.Features.Enabled.Kline.ResultLimit), 10), l.FormatExchangeKlineInterval(interval), strconv.FormatInt(dates[x].Start.UTC().Unix(), 10)) diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index bbd6c3d2..e9ac3184 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -21,7 +21,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -57,18 +56,11 @@ func (l *LocalBitcoins) SetDefaults() { l.API.CredentialsValidator.RequiresKey = true l.API.CredentialsValidator.RequiresSecret = true - l.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Uppercase: true} + configFmt := ¤cy.PairFormat{Uppercase: true} + err := l.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } l.Features = exchange.Features{ @@ -110,7 +102,6 @@ func (l *LocalBitcoins) Setup(exch *config.ExchangeConfig) error { l.SetEnabled(false) return nil } - return l.SetupDefaults(exch) } @@ -161,18 +152,24 @@ func (l *LocalBitcoins) UpdateTradablePairs(forceUpdate bool) error { if err != nil { return err } - return l.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return l.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (l *LocalBitcoins) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) tick, err := l.GetTicker() if err != nil { - return tickerPrice, err + return nil, err } - pairs := l.GetEnabledPairs(assetType) + pairs, err := l.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } for i := range pairs { curr := pairs[i].Quote.String() if _, ok := tick[curr]; !ok { @@ -182,10 +179,12 @@ func (l *LocalBitcoins) UpdateTicker(p currency.Pair, assetType asset.Item) (*ti tp.Pair = pairs[i] tp.Last = tick[curr].Avg24h tp.Volume = tick[curr].VolumeBTC + tp.ExchangeName = l.Name + tp.AssetType = assetType - err = ticker.ProcessTicker(l.Name, &tp, assetType) + err = ticker.ProcessTicker(&tp) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } @@ -432,11 +431,6 @@ func (l *LocalBitcoins) WithdrawFiatFundsToInternationalBank(withdrawRequest *wi return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (l *LocalBitcoins) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrFunctionNotSupported -} - // GetFeeByType returns an estimate of fee based on type of transaction func (l *LocalBitcoins) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if (!l.AllowAuthenticatedRequest() || l.SkipAuthCheck) && // Todo check connection status @@ -453,6 +447,11 @@ func (l *LocalBitcoins) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest return nil, err } + format, err := l.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range resp { orderDate, err := time.Parse(time.RFC3339, resp[i].Data.CreatedAt) @@ -480,7 +479,7 @@ func (l *LocalBitcoins) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest Side: side, Pair: currency.NewPairWithDelimiter(currency.BTC.String(), resp[i].Data.Currency, - l.GetPairFormat(asset.Spot, false).Delimiter), + format.Delimiter), Exchange: l.Name, }) } @@ -514,6 +513,11 @@ func (l *LocalBitcoins) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest } allTrades = append(allTrades, resp...) + format, err := l.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range allTrades { orderDate, err := time.Parse(time.RFC3339, allTrades[i].Data.CreatedAt) @@ -557,7 +561,7 @@ func (l *LocalBitcoins) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest Status: order.Status(status), Pair: currency.NewPairWithDelimiter(currency.BTC.String(), allTrades[i].Data.Currency, - l.GetPairFormat(asset.Spot, false).Delimiter), + format.Delimiter), Exchange: l.Name, }) } @@ -569,28 +573,6 @@ func (l *LocalBitcoins) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (l *LocalBitcoins) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (l *LocalBitcoins) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (l *LocalBitcoins) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (l *LocalBitcoins) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (l *LocalBitcoins) ValidateCredentials() error { diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go index 35dc8a68..9669a609 100644 --- a/exchanges/okcoin/okcoin_test.go +++ b/exchanges/okcoin/okcoin_test.go @@ -6,7 +6,6 @@ import ( "net/http" "os" "strings" - "sync" "testing" "time" @@ -21,7 +20,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/okgroup" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -69,14 +68,12 @@ func TestMain(m *testing.M) { okcoinConfig.API.Credentials.Secret = apiSecret okcoinConfig.API.Credentials.ClientID = passphrase okcoinConfig.API.Endpoints.WebsocketURL = o.API.Endpoints.WebsocketURL + o.Websocket = sharedtestvalues.NewTestWebsocket() err = o.Setup(okcoinConfig) if err != nil { log.Fatal("OKCoin setup error", err) } testSetupRan = true - o.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - o.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() - os.Exit(m.Run()) } @@ -752,33 +749,27 @@ func TestGetMarginTransactionDetails(t *testing.T) { // Will log in if credentials are present func TestSendWsMessages(t *testing.T) { if !o.Websocket.IsEnabled() && !o.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) + t.Skip(stream.WebsocketNotEnabled) } var ok bool - o.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: o.Name, - URL: o.Websocket.GetWebsocketURL(), - Verbose: o.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, - } var dialer websocket.Dialer - err := o.WebsocketConn.Dial(&dialer, http.Header{}) + err := o.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } - wg := sync.WaitGroup{} - wg.Add(1) - go o.WsReadData(&wg) - wg.Wait() - - subscription := wshandler.WebsocketChannelSubscription{ - Channel: "badChannel", + go o.WsReadData() + subscriptions := []stream.ChannelSubscription{ + { + Channel: "badChannel", + }, + } + err = o.Subscribe(subscriptions) + if err != nil { + t.Fatal(err) } - o.Subscribe(subscription) response := <-o.Websocket.DataHandler if err, ok = response.(error); ok && err != nil { - if !strings.Contains(response.(error).Error(), subscription.Channel) { + if !strings.Contains(response.(error).Error(), subscriptions[0].Channel) { t.Error("Expecting OKEX error - 30040 message: Channel badChannel doesn't exist") } } @@ -1099,18 +1090,24 @@ func TestGetOrderbook(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSDT") + currencyPair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } startTime := time.Unix(1588636800, 0) - _, err := o.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = o.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSDT") + currencyPair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } startTime := time.Unix(1588636800, 0) - _, err := o.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = o.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index 934493b0..a65fe8a9 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -15,8 +15,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/okgroup" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -55,23 +55,9 @@ func (o *OKCoin) SetDefaults() { o.API.CredentialsValidator.RequiresSecret = true o.API.CredentialsValidator.RequiresClientID = true - o.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Margin, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "-", - }, - - ConfigFormat: ¤cy.PairFormat{ - Uppercase: true, - Delimiter: "-", - }, - } + requestFmt := ¤cy.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter} + configFmt := ¤cy.PairFormat{Uppercase: true, Delimiter: currency.DashDelimiter} + o.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot, asset.Margin) o.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ @@ -152,7 +138,7 @@ func (o *OKCoin) SetDefaults() { o.API.Endpoints.URL = okCoinAPIURL o.API.Endpoints.WebsocketURL = okCoinWebsocketURL o.APIVersion = okCoinAPIVersion - o.Websocket = wshandler.New() + o.Websocket = stream.New() o.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit o.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout o.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -178,25 +164,56 @@ func (o *OKCoin) Run() { } forceUpdate := false - delim := o.GetPairFormat(asset.Spot, false).Delimiter - if !common.StringDataContains(o.CurrencyPairs.GetPairs(asset.Spot, - true).Strings(), delim) || - !common.StringDataContains(o.CurrencyPairs.GetPairs(asset.Spot, - false).Strings(), delim) { - enabledPairs := currency.NewPairsFromStrings( - []string{currency.BTC.String() + delim + currency.USD.String()}, - ) - log.Warnf(log.ExchangeSys, - "Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.\n", - o.Name) - forceUpdate = true + format, err := o.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + o.Name, + err) + return + } + enabled, err := o.CurrencyPairs.GetPairs(asset.Spot, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + o.Name, + err) + return + } - err := o.UpdatePairs(enabledPairs, asset.Spot, true, true) + avail, err := o.CurrencyPairs.GetPairs(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + o.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + + format.Delimiter + + currency.USD.String()}) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies.\n", o.Name) - return + } else { + log.Warnf(log.ExchangeSys, + "Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.\n", + o.Name) + forceUpdate = true + + err = o.UpdatePairs(p, asset.Spot, true, true) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies. Err: %s\n", + o.Name, + err) + return + } } } @@ -204,7 +221,7 @@ func (o *OKCoin) Run() { return } - err := o.UpdateTradablePairs(forceUpdate) + err = o.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", @@ -220,10 +237,15 @@ func (o *OKCoin) FetchTradablePairs(asset asset.Item) ([]string, error) { return nil, err } + format, err := o.GetPairFormat(asset, false) + if err != nil { + return nil, err + } + var pairs []string for x := range prods { pairs = append(pairs, prods[x].BaseCurrency+ - o.GetPairFormat(asset, false).Delimiter+ + format.Delimiter+ prods[x].QuoteCurrency) } @@ -237,40 +259,45 @@ func (o *OKCoin) UpdateTradablePairs(forceUpdate bool) error { if err != nil { return err } - - return o.UpdatePairs(currency.NewPairsFromStrings(pairs), - asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return o.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (o *OKCoin) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - var tickerData ticker.Price if assetType == asset.Spot { resp, err := o.GetSpotAllTokenPairsInformation() if err != nil { return nil, err } - pairs := o.GetEnabledPairs(assetType) + pairs, err := o.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } for i := range pairs { for j := range resp { if !pairs[i].Equal(resp[j].InstrumentID) { continue } - tickerData = ticker.Price{ - Last: resp[j].Last, - High: resp[j].High24h, - Low: resp[j].Low24h, - Bid: resp[j].BestBid, - Ask: resp[j].BestAsk, - Volume: resp[j].BaseVolume24h, - QuoteVolume: resp[j].QuoteVolume24h, - Open: resp[j].Open24h, - Pair: pairs[i], - LastUpdated: resp[j].Timestamp, - } - err = ticker.ProcessTicker(o.Name, &tickerData, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: resp[j].Last, + High: resp[j].High24h, + Low: resp[j].Low24h, + Bid: resp[j].BestBid, + Ask: resp[j].BestAsk, + Volume: resp[j].BaseVolume24h, + QuoteVolume: resp[j].QuoteVolume24h, + Open: resp[j].Open24h, + Pair: pairs[i], + LastUpdated: resp[j].Timestamp, + ExchangeName: o.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } @@ -295,12 +322,17 @@ func (o *OKCoin) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end } } + formattedPair, err := o.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + req := &okgroup.GetMarketDataRequest{ Asset: a, Start: start.UTC().Format(time.RFC3339), End: end.UTC().Format(time.RFC3339), Granularity: o.FormatExchangeKlineInterval(interval), - InstrumentID: o.FormatExchangeCurrency(pair, a).String(), + InstrumentID: formattedPair.String(), } candles, err := o.GetMarketData(req) @@ -371,13 +403,18 @@ func (o *OKCoin) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, st } dates := kline.CalcDateRanges(start, end, interval, o.Features.Enabled.Kline.ResultLimit) + formattedPair, err := o.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + for x := range dates { req := &okgroup.GetMarketDataRequest{ Asset: a, Start: dates[x].Start.UTC().Format(time.RFC3339), End: dates[x].End.UTC().Format(time.RFC3339), Granularity: o.FormatExchangeKlineInterval(interval), - InstrumentID: o.FormatExchangeCurrency(pair, a).String(), + InstrumentID: formattedPair.String(), } candles, err := o.GetMarketData(req) diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index b617be77..9985f346 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -8,7 +8,6 @@ import ( "os" "strconv" "strings" - "sync" "testing" "time" @@ -23,7 +22,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/okgroup" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -70,13 +69,11 @@ func TestMain(m *testing.M) { okexConfig.API.Credentials.Secret = apiSecret okexConfig.API.Credentials.ClientID = passphrase okexConfig.API.Endpoints.WebsocketURL = o.API.Endpoints.WebsocketURL + o.Websocket = sharedtestvalues.NewTestWebsocket() err = o.Setup(okexConfig) if err != nil { log.Fatal("Okex setup error", err) } - o.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - o.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() - os.Exit(m.Run()) } @@ -532,9 +529,12 @@ func TestGetSpotMarketData(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSDT") + currencyPair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } startTime := time.Unix(1588636800, 0) - _, err := o.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = o.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } @@ -549,7 +549,10 @@ func TestGetHistoricCandles(t *testing.T) { t.Fatal("unexpected result") } - swapPair := currency.NewPairFromString("BTC-USD_SWAP") + swapPair, err := currency.NewPairFromString("BTC-USD_SWAP") + if err != nil { + t.Fatal(err) + } _, err = o.GetHistoricCandles(swapPair, asset.PerpetualSwap, startTime, time.Now(), kline.OneDay) if err != nil { t.Fatal(err) @@ -557,9 +560,12 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCUSDT") + currencyPair, err := currency.NewPairFromString("BTCUSDT") + if err != nil { + t.Fatal(err) + } startTime := time.Unix(1588636800, 0) - _, err := o.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) + _, err = o.GetHistoricCandlesExtended(currencyPair, asset.Spot, startTime, time.Now(), kline.OneMin) if err != nil { t.Fatal(err) } @@ -1461,33 +1467,27 @@ func TestGetETTSettlementPriceHistory(t *testing.T) { // Will log in if credentials are present func TestSendWsMessages(t *testing.T) { if !o.Websocket.IsEnabled() && !o.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) + t.Skip(stream.WebsocketNotEnabled) } var ok bool - o.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: o.Name, - URL: o.Websocket.GetWebsocketURL(), - Verbose: o.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, - } var dialer websocket.Dialer - err := o.WebsocketConn.Dial(&dialer, http.Header{}) + err := o.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } - wg := sync.WaitGroup{} - wg.Add(1) - go o.WsReadData(&wg) - wg.Wait() - - subscription := wshandler.WebsocketChannelSubscription{ - Channel: "badChannel", + go o.WsReadData() + subscriptions := []stream.ChannelSubscription{ + { + Channel: "badChannel", + }, + } + err = o.Subscribe(subscriptions) + if err != nil { + t.Fatal(err) } - o.Subscribe(subscription) response := <-o.Websocket.DataHandler if err, ok = response.(error); ok && err != nil { - if !strings.Contains(response.(error).Error(), subscription.Channel) { + if !strings.Contains(response.(error).Error(), subscriptions[0].Channel) { t.Error("Expecting OKEX error - 30040 message: Channel badChannel doesn't exist") } } @@ -1821,6 +1821,13 @@ func TestGetOrderbook(t *testing.T) { } } +func TestUpdateTradablePairs(t *testing.T) { + err := o.UpdateTradablePairs(true) + if err != nil { + t.Fatal(err) + } +} + func TestWsSubscribe(t *testing.T) { pressXToJSON := []byte(`{"event":"subscribe","channel":"spot/ticker:ETH-USDT"}`) err := o.WsHandleData(pressXToJSON) diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index 2f83ac16..989dee68 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -17,16 +17,11 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/okgroup" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) -const ( - delimiterDash = "-" - delimiterUnderscore = "_" -) - // GetDefaultConfig returns a default exchange config func (o *OKEX) GetDefaultConfig() (*config.ExchangeConfig, error) { o.SetDefaults() @@ -61,32 +56,43 @@ func (o *OKEX) SetDefaults() { o.API.CredentialsValidator.RequiresSecret = true o.API.CredentialsValidator.RequiresClientID = true - o.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - asset.Futures, - asset.PerpetualSwap, - asset.Index, - }, - } // Same format used for perpetual swap and futures - fmt1 := currency.PairStore{ + futures := currency.PairStore{ RequestFormat: ¤cy.PairFormat{ Uppercase: true, - Delimiter: delimiterDash, + Delimiter: currency.DashDelimiter, }, ConfigFormat: ¤cy.PairFormat{ Uppercase: true, - Delimiter: delimiterUnderscore, + Delimiter: currency.UnderscoreDelimiter, }, } - o.CurrencyPairs.Store(asset.PerpetualSwap, fmt1) - o.CurrencyPairs.Store(asset.Futures, fmt1) + + swap := currency.PairStore{ + RequestFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: currency.DashDelimiter, + }, + ConfigFormat: ¤cy.PairFormat{ + Uppercase: true, + Delimiter: currency.UnderscoreDelimiter, + }, + } + + err := o.StoreAssetPairFormat(asset.PerpetualSwap, swap) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + err = o.StoreAssetPairFormat(asset.Futures, futures) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } index := currency.PairStore{ RequestFormat: ¤cy.PairFormat{ Uppercase: true, - Delimiter: delimiterDash, + Delimiter: currency.DashDelimiter, }, ConfigFormat: ¤cy.PairFormat{ Uppercase: true, @@ -96,15 +102,23 @@ func (o *OKEX) SetDefaults() { spot := currency.PairStore{ RequestFormat: ¤cy.PairFormat{ Uppercase: true, - Delimiter: delimiterDash, + Delimiter: currency.DashDelimiter, }, ConfigFormat: ¤cy.PairFormat{ Uppercase: true, - Delimiter: delimiterDash, + Delimiter: currency.DashDelimiter, }, } - o.CurrencyPairs.Store(asset.Spot, spot) - o.CurrencyPairs.Store(asset.Index, index) + + err = o.StoreAssetPairFormat(asset.Spot, spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } + + err = o.StoreAssetPairFormat(asset.Index, index) + if err != nil { + log.Errorln(log.ExchangeSys, err) + } o.Features = exchange.Features{ Supports: exchange.FeaturesSupported{ @@ -184,7 +198,7 @@ func (o *OKEX) SetDefaults() { o.API.Endpoints.URLDefault = okExAPIURL o.API.Endpoints.URL = okExAPIURL o.API.Endpoints.WebsocketURL = OkExWebsocketURL - o.Websocket = wshandler.New() + o.Websocket = stream.New() o.APIVersion = okExAPIVersion o.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit o.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout @@ -210,24 +224,57 @@ func (o *OKEX) Run() { o.API.Endpoints.WebsocketURL) } - delim := o.GetPairFormat(asset.Spot, false).Delimiter - forceUpdate := false - if !common.StringDataContains(o.GetEnabledPairs(asset.Spot).Strings(), delim) || - !common.StringDataContains(o.GetAvailablePairs(asset.Spot).Strings(), delim) { - forceUpdate = true - enabledPairs := currency.NewPairsFromStrings( - []string{currency.BTC.String() + delim + currency.USDT.String()}, - ) - log.Warnf(log.ExchangeSys, - "Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.", - o.Name) + format, err := o.GetPairFormat(asset.Spot, false) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + o.Name, + err) + return + } - err := o.UpdatePairs(enabledPairs, asset.Spot, true, forceUpdate) + forceUpdate := false + enabled, err := o.GetEnabledPairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + o.Name, + err) + return + } + + avail, err := o.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + o.Name, + err) + return + } + + if !common.StringDataContains(enabled.Strings(), format.Delimiter) || + !common.StringDataContains(avail.Strings(), format.Delimiter) { + forceUpdate = true + var p currency.Pairs + p, err = currency.NewPairsFromStrings([]string{currency.BTC.String() + + format.Delimiter + + currency.USDT.String()}) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update currencies.\n", o.Name) - return + } else { + log.Warnf(log.ExchangeSys, + "Enabled pairs for %v reset due to config upgrade, please enable the ones you would like again.", + o.Name) + + err = o.UpdatePairs(p, asset.Spot, true, forceUpdate) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update currencies.\n", + o.Name) + return + } } } @@ -235,7 +282,7 @@ func (o *OKEX) Run() { return } - err := o.UpdateTradablePairs(forceUpdate) + err = o.UpdateTradablePairs(forceUpdate) if err != nil { log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", @@ -247,6 +294,12 @@ func (o *OKEX) Run() { // FetchTradablePairs returns a list of the exchanges tradable pairs func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) { var pairs []string + + format, err := o.GetPairFormat(i, false) + if err != nil { + return nil, err + } + switch i { case asset.Spot: prods, err := o.GetSpotTokenPairDetails() @@ -258,7 +311,7 @@ func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) { pairs = append(pairs, currency.NewPairWithDelimiter(prods[x].BaseCurrency, prods[x].QuoteCurrency, - o.GetPairFormat(i, false).Delimiter).String()) + format.Delimiter).String()) } return pairs, nil case asset.Futures: @@ -268,9 +321,8 @@ func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) { } for x := range prods { - p := strings.Split(prods[x].InstrumentID, delimiterDash) - pairs = append(pairs, - p[0]+delimiterDash+p[1]+o.GetPairFormat(i, false).Delimiter+p[2]) + p := strings.Split(prods[x].InstrumentID, currency.DashDelimiter) + pairs = append(pairs, p[0]+currency.DashDelimiter+p[1]+format.Delimiter+p[2]) } return pairs, nil @@ -283,9 +335,9 @@ func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) { for x := range prods { pairs = append(pairs, prods[x].UnderlyingIndex+ - delimiterDash+ + currency.DashDelimiter+ prods[x].QuoteCurrency+ - o.GetPairFormat(i, false).Delimiter+ + format.Delimiter+ "SWAP") } return pairs, nil @@ -300,34 +352,62 @@ func (o *OKEX) FetchTradablePairs(i asset.Item) ([]string, error) { // UpdateTradablePairs updates the exchanges available pairs and stores // them in the exchanges config func (o *OKEX) UpdateTradablePairs(forceUpdate bool) error { - for x := range o.CurrencyPairs.AssetTypes { - if o.CurrencyPairs.AssetTypes[x] == asset.Index { + assets := o.CurrencyPairs.GetAssetTypes() + for x := range assets { + if assets[x] == asset.Index { // Update from futures continue } - pairs, err := o.FetchTradablePairs(o.CurrencyPairs.AssetTypes[x]) + pairs, err := o.FetchTradablePairs(assets[x]) if err != nil { return err } - if o.CurrencyPairs.AssetTypes[x] == asset.Futures { + if assets[x] == asset.Futures { var indexPairs []string + var futuresContracts []string for i := range pairs { - indexPairs = append(indexPairs, - strings.Split(pairs[i], delimiterUnderscore)[0]) + item := strings.Split(pairs[i], currency.UnderscoreDelimiter)[0] + futuresContracts = append(futuresContracts, pairs[i]) + if common.StringDataContains(indexPairs, item) { + continue + } + indexPairs = append(indexPairs, item) } - err = o.UpdatePairs(currency.NewPairsFromStrings(indexPairs), - asset.Index, - false, - forceUpdate) + var indexPair currency.Pairs + indexPair, err = currency.NewPairsFromStrings(indexPairs) if err != nil { return err } + + err = o.UpdatePairs(indexPair, asset.Index, false, forceUpdate) + if err != nil { + return err + } + + var futurePairs currency.Pairs + for i := range futuresContracts { + var c currency.Pair + c, err = currency.NewPairDelimiter(futuresContracts[i], currency.UnderscoreDelimiter) + if err != nil { + return err + } + futurePairs = append(futurePairs, c) + } + + err = o.UpdatePairs(futurePairs, asset.Futures, false, forceUpdate) + if err != nil { + return err + } + continue + } + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err } - err = o.UpdatePairs(currency.NewPairsFromStrings(pairs), - o.CurrencyPairs.AssetTypes[x], false, forceUpdate) + err = o.UpdatePairs(p, assets[x], false, forceUpdate) if err != nil { return err } @@ -344,25 +424,32 @@ func (o *OKEX) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Pric if err != nil { return tickerPrice, err } + + enabled, err := o.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + for j := range resp { - if !o.GetEnabledPairs(assetType).Contains(resp[j].InstrumentID, true) { + if !enabled.Contains(resp[j].InstrumentID, true) { continue } - tickerPrice = &ticker.Price{ - Last: resp[j].Last, - High: resp[j].High24h, - Low: resp[j].Low24h, - Bid: resp[j].BestBid, - Ask: resp[j].BestAsk, - Volume: resp[j].BaseVolume24h, - QuoteVolume: resp[j].QuoteVolume24h, - Open: resp[j].Open24h, - Pair: resp[j].InstrumentID, - LastUpdated: resp[j].Timestamp, - } - err = ticker.ProcessTicker(o.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: resp[j].Last, + High: resp[j].High24h, + Low: resp[j].Low24h, + Bid: resp[j].BestBid, + Ask: resp[j].BestAsk, + Volume: resp[j].BaseVolume24h, + QuoteVolume: resp[j].QuoteVolume24h, + Open: resp[j].Open24h, + Pair: resp[j].InstrumentID, + LastUpdated: resp[j].Timestamp, + ExchangeName: o.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } @@ -372,27 +459,33 @@ func (o *OKEX) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Pric return nil, err } + enabled, err := o.GetEnabledPairs(asset.PerpetualSwap) + if err != nil { + return nil, err + } + for j := range resp { - p := strings.Split(resp[j].InstrumentID, delimiterDash) - nC := currency.NewPairWithDelimiter(p[0]+delimiterDash+p[1], + p := strings.Split(resp[j].InstrumentID, currency.DashDelimiter) + nC := currency.NewPairWithDelimiter(p[0]+currency.DashDelimiter+p[1], p[2], - delimiterUnderscore) - if !o.GetEnabledPairs(assetType).Contains(nC, true) { + currency.UnderscoreDelimiter) + if !enabled.Contains(nC, true) { continue } - tickerPrice = &ticker.Price{ - Last: resp[j].Last, - High: resp[j].High24H, - Low: resp[j].Low24H, - Bid: resp[j].BestBid, - Ask: resp[j].BestAsk, - Volume: resp[j].Volume24H, - Pair: nC, - LastUpdated: resp[j].Timestamp, - } - err = ticker.ProcessTicker(o.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: resp[j].Last, + High: resp[j].High24H, + Low: resp[j].Low24H, + Bid: resp[j].BestBid, + Ask: resp[j].BestAsk, + Volume: resp[j].Volume24H, + Pair: nC, + LastUpdated: resp[j].Timestamp, + ExchangeName: o.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } @@ -402,27 +495,33 @@ func (o *OKEX) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Pric return nil, err } + enabled, err := o.GetEnabledPairs(asset.Futures) + if err != nil { + return nil, err + } + for j := range resp { - p := strings.Split(resp[j].InstrumentID, delimiterDash) - nC := currency.NewPairWithDelimiter(p[0]+delimiterDash+p[1], + p := strings.Split(resp[j].InstrumentID, currency.DashDelimiter) + nC := currency.NewPairWithDelimiter(p[0]+currency.DashDelimiter+p[1], p[2], - delimiterUnderscore) - if !o.GetEnabledPairs(assetType).Contains(nC, true) { + currency.UnderscoreDelimiter) + if !enabled.Contains(nC, true) { continue } - tickerPrice = &ticker.Price{ - Last: resp[j].Last, - High: resp[j].High24h, - Low: resp[j].Low24h, - Bid: resp[j].BestBid, - Ask: resp[j].BestAsk, - Volume: resp[j].Volume24h, - Pair: nC, - LastUpdated: resp[j].Timestamp, - } - err = ticker.ProcessTicker(o.Name, tickerPrice, assetType) + + err = ticker.ProcessTicker(&ticker.Price{ + Last: resp[j].Last, + High: resp[j].High24h, + Low: resp[j].Low24h, + Bid: resp[j].BestBid, + Ask: resp[j].BestAsk, + Volume: resp[j].Volume24h, + Pair: nC, + LastUpdated: resp[j].Timestamp, + ExchangeName: o.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } } @@ -450,12 +549,17 @@ func (o *OKEX) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end t } } + formattedPair, err := o.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + req := &okgroup.GetMarketDataRequest{ Asset: a, Start: start.UTC().Format(time.RFC3339), End: end.UTC().Format(time.RFC3339), Granularity: o.FormatExchangeKlineInterval(interval), - InstrumentID: o.FormatExchangeCurrency(pair, a).String(), + InstrumentID: formattedPair.String(), } candles, err := o.GetMarketData(req) @@ -527,13 +631,17 @@ func (o *OKEX) GetHistoricCandlesExtended(pair currency.Pair, a asset.Item, star } dates := kline.CalcDateRanges(start, end, interval, o.Features.Enabled.Kline.ResultLimit) + formattedPair, err := o.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } for x := range dates { req := &okgroup.GetMarketDataRequest{ Asset: a, Start: dates[x].Start.UTC().Format(time.RFC3339), End: dates[x].End.UTC().Format(time.RFC3339), Granularity: o.FormatExchangeKlineInterval(interval), - InstrumentID: o.FormatExchangeCurrency(pair, a).String(), + InstrumentID: formattedPair.String(), } candles, err := o.GetMarketData(req) diff --git a/exchanges/okgroup/okgroup.go b/exchanges/okgroup/okgroup.go index c0eb4c1d..9137f9c0 100644 --- a/exchanges/okgroup/okgroup.go +++ b/exchanges/okgroup/okgroup.go @@ -18,7 +18,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -89,8 +88,7 @@ var errMissValue = errors.New("warning - resp value is missing from exchange") // OKGroup is the overaching type across the all of OKEx's exchange methods type OKGroup struct { exchange.Base - ExchangeName string - WebsocketConn *wshandler.WebsocketConnection + ExchangeName string // Spot and contract market error codes as per https://www.okex.com/rest_request.html ErrorCodes map[string]error // Stores for corresponding variable checks diff --git a/exchanges/okgroup/okgroup_types.go b/exchanges/okgroup/okgroup_types.go index 3f70b66d..66d6a611 100644 --- a/exchanges/okgroup/okgroup_types.go +++ b/exchanges/okgroup/okgroup_types.go @@ -1317,19 +1317,22 @@ type WebsocketDataResponse struct { type WebsocketTickerData struct { Table string `json:"table"` Data []struct { - BaseVolume24h float64 `json:"base_volume_24h,string"` - BestAsk float64 `json:"best_ask,string"` - BestAskSize float64 `json:"best_ask_size,string"` - BestBid float64 `json:"best_bid,string"` - BestBidSize float64 `json:"best_bid_size,string"` - High24h float64 `json:"high_24h,string"` - InstrumentID string `json:"instrument_id"` - Last float64 `json:"last,string"` - LastQty float64 `json:"last_qty,string"` - Low24h float64 `json:"low_24h,string"` - Open24h float64 `json:"open_24h,string"` - QuoteVolume24h float64 `json:"quote_volume_24h,string"` - Timestamp time.Time `json:"timestamp"` + BaseVolume24h float64 `json:"base_volume_24h,string"` + BestAsk float64 `json:"best_ask,string"` + BestAskSize float64 `json:"best_ask_size,string"` + BestBid float64 `json:"best_bid,string"` + BestBidSize float64 `json:"best_bid_size,string"` + High24h float64 `json:"high_24h,string"` + InstrumentID string `json:"instrument_id"` + Last float64 `json:"last,string"` + LastQty float64 `json:"last_qty,string"` + Low24h float64 `json:"low_24h,string"` + Open24h float64 `json:"open_24h,string"` + QuoteVolume24h float64 `json:"quote_volume_24h,string"` + Timestamp time.Time `json:"timestamp"` + ContractVolume24h float64 `json:"volume_24h,string"` + TokenVolume24h float64 `json:"volume_token_24h,string"` + OpenInterest float64 `json:"open_interest,string"` } `json:"data"` } @@ -1343,6 +1346,8 @@ type WebsocketTradeResponse struct { Side string `json:"side"` Timestamp time.Time `json:"timestamp"` TradeID string `json:"trade_id"` + // Quantity - Futures amount is sent as a separate json field + Quantity float64 `json:"qty,string"` } `json:"data"` } diff --git a/exchanges/okgroup/okgroup_websocket.go b/exchanges/okgroup/okgroup_websocket.go index e2a69298..9e97877b 100644 --- a/exchanges/okgroup/okgroup_websocket.go +++ b/exchanges/okgroup/okgroup_websocket.go @@ -18,9 +18,9 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -147,6 +147,8 @@ const ( delimiterColon = ":" delimiterDash = "-" delimiterUnderscore = "_" + + maxConnByteLen = 4096 ) // orderbookMutex Ensures if two entries arrive at once, only one can be @@ -176,10 +178,12 @@ var defaultSwapSubscribedChannels = []string{okGroupWsSwapDepth, // WsConnect initiates a websocket connection func (o *OKGroup) WsConnect() error { if !o.Websocket.IsEnabled() || !o.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := o.WebsocketConn.Dial(&dialer, http.Header{}) + dialer.ReadBufferSize = 8192 + dialer.WriteBufferSize = 8192 + err := o.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } @@ -187,9 +191,8 @@ func (o *OKGroup) WsConnect() error { log.Debugf(log.ExchangeSys, "Successful connection to %v\n", o.Websocket.GetWebsocketURL()) } - wg := sync.WaitGroup{} - wg.Add(1) - go o.WsReadData(&wg) + + go o.WsReadData() if o.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { err = o.WsLogin() if err != nil { @@ -200,10 +203,11 @@ func (o *OKGroup) WsConnect() error { } } - o.GenerateDefaultSubscriptions() - // Ensures that we start the routines and we dont race when shutdown occurs - wg.Wait() - return nil + subs, err := o.GenerateDefaultSubscriptions() + if err != nil { + return err + } + return o.Websocket.SubscribeToChannels(subs) } // WsLogin sends a login request to websocket to enable access to authenticated endpoints @@ -225,7 +229,7 @@ func (o *OKGroup) WsLogin() error { base64, }, } - err := o.WebsocketConn.SendJSONMessage(request) + _, err := o.Websocket.Conn.SendMessageReturnResponse("login", request) if err != nil { o.Websocket.SetCanUseAuthenticatedEndpoints(false) return err @@ -234,28 +238,18 @@ func (o *OKGroup) WsLogin() error { } // WsReadData receives and passes on websocket messages for processing -func (o *OKGroup) WsReadData(wg *sync.WaitGroup) { +func (o *OKGroup) WsReadData() { o.Websocket.Wg.Add(1) - defer func() { - o.Websocket.Wg.Done() - }() - wg.Done() + defer o.Websocket.Wg.Done() for { - select { - case <-o.Websocket.ShutdownC: + resp := o.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := o.WebsocketConn.ReadMessage() - if err != nil { - o.Websocket.ReadMessageErrors <- err - return - } - o.Websocket.TrafficAlert <- struct{}{} - err = o.WsHandleData(resp.Raw) - if err != nil { - o.Websocket.DataHandler <- err - } + } + err := o.WsHandleData(resp.Raw) + if err != nil { + o.Websocket.DataHandler <- err } } } @@ -283,7 +277,9 @@ func (o *OKGroup) WsHandleData(respRaw []byte) error { case okGroupWsOrder: return o.wsProcessOrder(respRaw) } - o.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: o.Name + wshandler.UnhandledMessage + string(respRaw)} + o.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: o.Name + stream.UnhandledMessage + string(respRaw), + } return nil } @@ -299,7 +295,9 @@ func (o *OKGroup) WsHandleData(respRaw []byte) error { err = json.Unmarshal(respRaw, &eventResponse) if err == nil && eventResponse.Event != "" { if eventResponse.Event == "login" { - o.Websocket.SetCanUseAuthenticatedEndpoints(eventResponse.Success) + if o.Websocket.Match.Incoming("login") { + o.Websocket.SetCanUseAuthenticatedEndpoints(eventResponse.Success) + } } if o.Verbose { log.Debug(log.ExchangeSys, @@ -365,6 +363,16 @@ func (o *OKGroup) wsProcessOrder(respRaw []byte) error { Err: err, } } + + pair, err := currency.NewPairFromString(resp.Data[i].InstrumentID) + if err != nil { + o.Websocket.DataHandler <- order.ClassificationError{ + Exchange: o.Name, + OrderID: resp.Data[i].OrderID, + Err: err, + } + } + o.Websocket.DataHandler <- &order.Detail{ ImmediateOrCancel: resp.Data[i].OrderType == 3, FillOrKill: resp.Data[i].OrderType == 2, @@ -380,7 +388,7 @@ func (o *OKGroup) wsProcessOrder(respRaw []byte) error { Status: oStatus, AssetType: o.GetAssetTypeFromTableName(resp.Table), Date: resp.Data[i].CreatedAt, - Pair: currency.NewPairFromString(resp.Data[i].InstrumentID), + Pair: pair, } } return nil @@ -393,23 +401,36 @@ func (o *OKGroup) wsProcessTickers(respRaw []byte) error { if err != nil { return err } + a := o.GetAssetTypeFromTableName(response.Table) for i := range response.Data { - a := o.GetAssetTypeFromTableName(response.Table) + f := strings.Split(response.Data[i].InstrumentID, delimiterDash) + var c currency.Pair switch a { case asset.Futures, asset.PerpetualSwap: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) - c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], f[2], delimiterUnderscore) + c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], + f[2], + delimiterUnderscore) default: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash) } + + baseVolume := response.Data[i].BaseVolume24h + if response.Data[i].ContractVolume24h != 0 { + baseVolume = response.Data[i].ContractVolume24h + } + + quoteVolume := response.Data[i].QuoteVolume24h + if response.Data[i].TokenVolume24h != 0 { + quoteVolume = response.Data[i].TokenVolume24h + } + o.Websocket.DataHandler <- &ticker.Price{ ExchangeName: o.Name, Open: response.Data[i].Open24h, Close: response.Data[i].Last, - Volume: response.Data[i].BaseVolume24h, - QuoteVolume: response.Data[i].QuoteVolume24h, + Volume: baseVolume, + QuoteVolume: quoteVolume, High: response.Data[i].High24h, Low: response.Data[i].Low24h, Bid: response.Data[i].BestBid, @@ -430,17 +451,21 @@ func (o *OKGroup) wsProcessTrades(respRaw []byte) error { if err != nil { return err } + + a := o.GetAssetTypeFromTableName(response.Table) for i := range response.Data { - a := o.GetAssetTypeFromTableName(response.Table) + f := strings.Split(response.Data[i].InstrumentID, delimiterDash) + var c currency.Pair switch a { case asset.Futures, asset.PerpetualSwap: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) - c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], f[2], delimiterUnderscore) + c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], + f[2], + delimiterUnderscore) default: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash) } + tSide, err := order.StringToOrderSide(response.Data[i].Side) if err != nil { o.Websocket.DataHandler <- order.ClassificationError{ @@ -448,8 +473,14 @@ func (o *OKGroup) wsProcessTrades(respRaw []byte) error { Err: err, } } - o.Websocket.DataHandler <- wshandler.TradeData{ - Amount: response.Data[i].Size, + + amount := response.Data[i].Size + if response.Data[i].Quantity != 0 { + amount = response.Data[i].Quantity + } + + o.Websocket.DataHandler <- stream.TradeData{ + Amount: amount, AssetType: o.GetAssetTypeFromTableName(response.Table), CurrencyPair: c, Exchange: o.Name, @@ -468,15 +499,18 @@ func (o *OKGroup) wsProcessCandles(respRaw []byte) error { if err != nil { return err } + + a := o.GetAssetTypeFromTableName(response.Table) for i := range response.Data { - a := o.GetAssetTypeFromTableName(response.Table) + f := strings.Split(response.Data[i].InstrumentID, delimiterDash) + var c currency.Pair switch a { case asset.Futures, asset.PerpetualSwap: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) - c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], f[2], delimiterUnderscore) + c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], + f[2], + delimiterUnderscore) default: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash) } @@ -489,13 +523,9 @@ func (o *OKGroup) wsProcessCandles(respRaw []byte) error { } candleIndex := strings.LastIndex(response.Table, okGroupWsCandle) - secondIndex := strings.LastIndex(response.Table, "0s") - candleInterval := "" - if candleIndex > 0 || secondIndex > 0 { - candleInterval = response.Table[candleIndex+len(okGroupWsCandle) : secondIndex] - } + candleInterval := response.Table[candleIndex+len(okGroupWsCandle):] - klineData := wshandler.KlineData{ + klineData := stream.KlineData{ AssetType: o.GetAssetTypeFromTableName(response.Table), Pair: c, Exchange: o.Name, @@ -522,7 +552,6 @@ func (o *OKGroup) wsProcessCandles(respRaw []byte) error { if err != nil { return err } - o.Websocket.DataHandler <- klineData } return nil @@ -537,22 +566,27 @@ func (o *OKGroup) WsProcessOrderBook(respRaw []byte) error { } orderbookMutex.Lock() defer orderbookMutex.Unlock() + a := o.GetAssetTypeFromTableName(response.Table) for i := range response.Data { - a := o.GetAssetTypeFromTableName(response.Table) + f := strings.Split(response.Data[i].InstrumentID, delimiterDash) + var c currency.Pair switch a { case asset.Futures, asset.PerpetualSwap: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) - c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], f[2], delimiterUnderscore) + c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], + f[2], + delimiterUnderscore) default: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash) } if response.Action == okGroupWsOrderbookPartial { err := o.WsProcessPartialOrderBook(&response.Data[i], c, a) if err != nil { - o.wsResubscribeToOrderbook(&response) + err2 := o.wsResubscribeToOrderbook(&response) + if err2 != nil { + o.Websocket.DataHandler <- err2 + } return err } } else if response.Action == okGroupWsOrderbookUpdate { @@ -561,7 +595,10 @@ func (o *OKGroup) WsProcessOrderBook(respRaw []byte) error { } err := o.WsProcessUpdateOrderbook(&response.Data[i], c, a) if err != nil { - o.wsResubscribeToOrderbook(&response) + err2 := o.wsResubscribeToOrderbook(&response) + if err2 != nil { + o.Websocket.DataHandler <- err2 + } return err } } @@ -569,28 +606,34 @@ func (o *OKGroup) WsProcessOrderBook(respRaw []byte) error { return nil } -func (o *OKGroup) wsResubscribeToOrderbook(response *WebsocketOrderBooksData) { +func (o *OKGroup) wsResubscribeToOrderbook(response *WebsocketOrderBooksData) error { + a := o.GetAssetTypeFromTableName(response.Table) for i := range response.Data { - a := o.GetAssetTypeFromTableName(response.Table) + f := strings.Split(response.Data[i].InstrumentID, delimiterDash) + var c currency.Pair switch a { case asset.Futures, asset.PerpetualSwap: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) c = currency.NewPairWithDelimiter(f[0]+delimiterDash+f[1], f[2], delimiterDash) default: - f := strings.Split(response.Data[i].InstrumentID, delimiterDash) c = currency.NewPairWithDelimiter(f[0], f[1], delimiterDash) } - channelToResubscribe := wshandler.WebsocketChannelSubscription{ + channelToResubscribe := &stream.ChannelSubscription{ Channel: response.Table, Currency: c, + Asset: a, + } + err := o.Websocket.ResubscribeToChannel(channelToResubscribe) + if err != nil { + return fmt.Errorf("%s resubscribe to orderbook error %s", o.Name, err) } - o.Websocket.ResubscribeToChannel(channelToResubscribe) } + return nil } -// AppendWsOrderbookItems adds websocket orderbook data bid/asks into an orderbook item array +// AppendWsOrderbookItems adds websocket orderbook data bid/asks into an +// orderbook item array func (o *OKGroup) AppendWsOrderbookItems(entries [][]interface{}) ([]orderbook.Item, error) { var items []orderbook.Item for j := range entries { @@ -607,8 +650,8 @@ func (o *OKGroup) AppendWsOrderbookItems(entries [][]interface{}) ([]orderbook.I return items, nil } -// WsProcessPartialOrderBook takes websocket orderbook data and creates an orderbook -// Calculates checksum to ensure it is valid +// WsProcessPartialOrderBook takes websocket orderbook data and creates an +// orderbook Calculates checksum to ensure it is valid func (o *OKGroup) WsProcessPartialOrderBook(wsEventData *WebsocketOrderBook, instrument currency.Pair, a asset.Item) error { signedChecksum := o.CalculatePartialOrderbookChecksum(wsEventData) if signedChecksum != wsEventData.Checksum { @@ -642,24 +685,14 @@ func (o *OKGroup) WsProcessPartialOrderBook(wsEventData *WebsocketOrderBook, ins Pair: instrument, ExchangeName: o.Name, } - - err = o.Websocket.Orderbook.LoadSnapshot(&newOrderBook) - if err != nil { - return err - } - - o.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: o.Name, - Asset: a, - Pair: instrument, - } - return nil + return o.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } // WsProcessUpdateOrderbook updates an existing orderbook using websocket data -// After merging WS data, it will sort, validate and finally update the existing orderbook +// After merging WS data, it will sort, validate and finally update the existing +// orderbook func (o *OKGroup) WsProcessUpdateOrderbook(wsEventData *WebsocketOrderBook, instrument currency.Pair, a asset.Item) error { - update := wsorderbook.WebsocketOrderbookUpdate{ + update := buffer.Update{ Asset: a, Pair: instrument, UpdateTime: wsEventData.Timestamp, @@ -690,13 +723,6 @@ func (o *OKGroup) WsProcessUpdateOrderbook(wsEventData *WebsocketOrderBook, inst wsEventData.InstrumentID) return errors.New("checksum failed") } - - o.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: o.Name, - Asset: a, - Pair: instrument, - } - return nil } @@ -750,95 +776,134 @@ func (o *OKGroup) CalculateUpdateOrderbookChecksum(orderbookData *orderbook.Base // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be // handled by ManageSubscriptions() -func (o *OKGroup) GenerateDefaultSubscriptions() { - var subscriptions []wshandler.WebsocketChannelSubscription +func (o *OKGroup) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription assets := o.GetAssetTypes() for x := range assets { - enabledCurrencies := o.GetEnabledPairs(assets[x]) - if len(enabledCurrencies) == 0 { - continue + pairs, err := o.GetEnabledPairs(assets[x]) + if err != nil { + return nil, err } switch assets[x] { case asset.Spot: - for i := range enabledCurrencies { - for y := range defaultSpotSubscribedChannels { - subscriptions = append(subscriptions, - wshandler.WebsocketChannelSubscription{ - Channel: defaultSpotSubscribedChannels[y], - Currency: o.FormatExchangeCurrency(enabledCurrencies[i], - asset.Spot), - }) - } + channels := defaultSpotSubscribedChannels + if o.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + channels = append(channels, + okGroupWsSpotMarginAccount, + okGroupWsSpotAccount, + okGroupWsSpotOrder) } - if o.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - subscriptions = append(subscriptions, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsSpotMarginAccount, - }, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsSpotAccount, - }, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsSpotOrder, - }) + for i := range pairs { + p, err := o.FormatExchangeCurrency(pairs[i], asset.Spot) + if err != nil { + return nil, err + } + for y := range channels { + subscriptions = append(subscriptions, + stream.ChannelSubscription{ + Channel: channels[y], + Currency: p, + Asset: asset.Spot, + }) + } } case asset.Futures: - for i := range enabledCurrencies { - for y := range defaultFuturesSubscribedChannels { + channels := defaultFuturesSubscribedChannels + if o.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + channels = append(channels, + okGroupWsFuturesAccount, + okGroupWsFuturesPosition, + okGroupWsFuturesOrder) + } + var futuresAccountPairs currency.Pairs + var futuresAccountCodes currency.Currencies + + for i := range pairs { + p, err := o.FormatExchangeCurrency(pairs[i], asset.Futures) + if err != nil { + return nil, err + } + for y := range channels { + if channels[y] == okGroupWsFuturesAccount { + currencyString := strings.Split(pairs[i].String(), + currency.UnderscoreDelimiter)[0] + newP, err := currency.NewPairFromString(currencyString) + if err != nil { + return nil, err + } + + if !futuresAccountCodes.Contains(newP.Base) { + // subscribe to coin-margin futures trading mode + subscriptions = append(subscriptions, + stream.ChannelSubscription{ + Channel: channels[y], + Currency: currency.NewPair(newP.Base, currency.Code{}), + Asset: asset.Futures, + }) + futuresAccountCodes = append(futuresAccountCodes, newP.Base) + } + + if newP.Quote != currency.USDT { + // Only allows subscription to USDT margined pair + continue + } + + if !futuresAccountPairs.Contains(newP, true) { + subscriptions = append(subscriptions, + stream.ChannelSubscription{ + Channel: channels[y], + Currency: newP, + Asset: asset.Futures, + }) + futuresAccountPairs = futuresAccountPairs.Add(newP) + } + + continue + } subscriptions = append(subscriptions, - wshandler.WebsocketChannelSubscription{ - Channel: defaultFuturesSubscribedChannels[y], - Currency: o.FormatExchangeCurrency(enabledCurrencies[i], - asset.Futures), + stream.ChannelSubscription{ + Channel: channels[y], + Currency: p, + Asset: asset.Futures, }) } } - - if o.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - subscriptions = append(subscriptions, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsFuturesAccount, - }, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsFuturesPosition, - }, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsFuturesOrder, - }) - } case asset.PerpetualSwap: - for i := range enabledCurrencies { - for y := range defaultSwapSubscribedChannels { + channels := defaultSwapSubscribedChannels + if o.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { + channels = append(channels, + okGroupWsSwapAccount, + okGroupWsSwapPosition, + okGroupWsSwapOrder) + } + for i := range pairs { + p, err := o.FormatExchangeCurrency(pairs[i], asset.PerpetualSwap) + if err != nil { + return nil, err + } + for y := range channels { subscriptions = append(subscriptions, - wshandler.WebsocketChannelSubscription{ - Channel: defaultSwapSubscribedChannels[y], - Currency: o.FormatExchangeCurrency(enabledCurrencies[i], - asset.PerpetualSwap), + stream.ChannelSubscription{ + Channel: channels[y], + Currency: p, + Asset: asset.PerpetualSwap, }) } } - - if o.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - subscriptions = append(subscriptions, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsSwapAccount, - }, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsSwapPosition, - }, - wshandler.WebsocketChannelSubscription{ - Channel: okGroupWsSwapOrder, - }) - } case asset.Index: - for i := range enabledCurrencies { + for i := range pairs { + p, err := o.FormatExchangeCurrency(pairs[i], asset.Index) + if err != nil { + return nil, err + } for y := range defaultIndexSubscribedChannels { subscriptions = append(subscriptions, - wshandler.WebsocketChannelSubscription{ + stream.ChannelSubscription{ Channel: defaultIndexSubscribedChannels[y], - Currency: o.FormatExchangeCurrency(enabledCurrencies[i], asset.Index), + Currency: p, + Asset: asset.Index, }) } } @@ -846,35 +911,84 @@ func (o *OKGroup) GenerateDefaultSubscriptions() { o.Websocket.DataHandler <- errors.New("unhandled asset type") } } - - o.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (o *OKGroup) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - c := channelToSubscribe.Currency.String() - request := WebsocketEventRequest{ - Operation: "subscribe", - Arguments: []string{channelToSubscribe.Channel + delimiterColon + c}, - } - if strings.EqualFold(channelToSubscribe.Channel, okGroupWsSpotAccount) { - request.Arguments = []string{channelToSubscribe.Channel + - delimiterColon + - channelToSubscribe.Currency.Base.String()} - } - - return o.WebsocketConn.SendJSONMessage(request) +func (o *OKGroup) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + return o.handleSubscriptions("subscribe", channelsToSubscribe) } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (o *OKGroup) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { +func (o *OKGroup) Unsubscribe(channelsToUnsubscribe []stream.ChannelSubscription) error { + return o.handleSubscriptions("unsubscribe", channelsToUnsubscribe) +} + +func (o *OKGroup) handleSubscriptions(operation string, subs []stream.ChannelSubscription) error { request := WebsocketEventRequest{ - Operation: "unsubscribe", - Arguments: []string{channelToSubscribe.Channel + - delimiterColon + - channelToSubscribe.Currency.String()}, + Operation: operation, } - return o.WebsocketConn.SendJSONMessage(request) + + var channels []stream.ChannelSubscription + for i := 0; i < len(subs); i++ { + // Temp type to evaluate max byte len after a marshal on batched unsubs + temp := WebsocketEventRequest{ + Operation: operation, + } + temp.Arguments = make([]string, len(request.Arguments)) + copy(temp.Arguments, request.Arguments) + + arg := subs[i].Channel + delimiterColon + if strings.EqualFold(subs[i].Channel, okGroupWsSpotAccount) { + arg += subs[i].Currency.Base.String() + } else { + arg += subs[i].Currency.String() + } + + temp.Arguments = append(temp.Arguments, arg) + chunk, err := json.Marshal(request) + if err != nil { + return err + } + + if len(chunk) > maxConnByteLen { + // If temp chunk exceeds max byte length determined by the exchange, + // commit last payload. + i-- // reverse position in range to reuse channel unsubscription on + // next iteration + err = o.Websocket.Conn.SendJSONMessage(request) + if err != nil { + return err + } + + if operation == "unsubscribe" { + o.Websocket.RemoveSuccessfulUnsubscriptions(channels...) + } else { + o.Websocket.AddSuccessfulSubscriptions(channels...) + } + + // Drop prior unsubs and chunked payload args on successful unsubscription + channels = nil + request.Arguments = nil + continue + } + // Add pending chained items + channels = append(channels, subs[i]) + request.Arguments = temp.Arguments + } + + // Commit left overs to payload + err := o.Websocket.Conn.SendJSONMessage(request) + if err != nil { + return err + } + + if operation == "unsubscribe" { + o.Websocket.RemoveSuccessfulUnsubscriptions(channels...) + } else { + o.Websocket.AddSuccessfulSubscriptions(channels...) + } + return nil } // GetWsChannelWithoutOrderType takes WebsocketDataResponse.Table and returns diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index d26ee67a..225cbd77 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -15,7 +15,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -35,7 +35,7 @@ func (o *OKGroup) Setup(exch *config.ExchangeConfig) error { return err } - err = o.Websocket.Setup(&wshandler.WebsocketSetup{ + err = o.Websocket.Setup(&stream.WebsocketSetup{ Enabled: exch.Features.Enabled.Websocket, Verbose: exch.Verbose, AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, @@ -46,30 +46,19 @@ func (o *OKGroup) Setup(exch *config.ExchangeConfig) error { Connector: o.WsConnect, Subscriber: o.Subscribe, UnSubscriber: o.Unsubscribe, + GenerateSubscriptions: o.GenerateDefaultSubscriptions, Features: &o.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, }) if err != nil { return err } - o.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: o.Name, - URL: o.Websocket.GetWebsocketURL(), - ProxyURL: o.Websocket.GetProxyAddress(), - Verbose: o.Verbose, + return o.Websocket.SetupNewConnection(stream.ConnectionSetup{ RateLimit: okGroupWsRateLimit, ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - o.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - false, - false, - false, - exch.Name) - return nil + }) } // FetchOrderbook returns orderbook base on the currency pair @@ -88,8 +77,13 @@ func (o *OKGroup) UpdateOrderbook(p currency.Pair, a asset.Item) (*orderbook.Bas return orderBook, errors.New("no orderbooks for index") } + fpair, err := o.FormatExchangeCurrency(p, a) + if err != nil { + return nil, err + } + orderbookNew, err := o.GetOrderBook(GetOrderBookRequest{ - InstrumentID: o.FormatExchangeCurrency(p, a).String(), + InstrumentID: fpair.String(), }, a) if err != nil { return orderBook, err @@ -268,15 +262,20 @@ func (o *OKGroup) GetExchangeHistory(p currency.Pair, assetType asset.Item, time } // SubmitOrder submits a new order -func (o *OKGroup) SubmitOrder(s *order.Submit) (resp order.SubmitResponse, err error) { - err = s.Validate() +func (o *OKGroup) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) { + err := s.Validate() if err != nil { - return resp, err + return order.SubmitResponse{}, err + } + + fpair, err := o.FormatExchangeCurrency(s.Pair, s.AssetType) + if err != nil { + return order.SubmitResponse{}, err } request := PlaceOrderRequest{ ClientOID: s.ClientID, - InstrumentID: o.FormatExchangeCurrency(s.Pair, asset.Spot).String(), + InstrumentID: fpair.String(), Side: s.Side.Lower(), Type: s.Type.Lower(), Size: strconv.FormatFloat(s.Amount, 'f', -1, 64), @@ -287,15 +286,17 @@ func (o *OKGroup) SubmitOrder(s *order.Submit) (resp order.SubmitResponse, err e orderResponse, err := o.PlaceSpotOrder(&request) if err != nil { - return + return order.SubmitResponse{}, err } + var resp order.SubmitResponse resp.IsOrderPlaced = orderResponse.Result resp.OrderID = orderResponse.OrderID if s.Type == order.Market { resp.FullyMatched = true } - return + + return resp, nil } // ModifyOrder will allow of changing orderbook placement and limit to @@ -310,11 +311,18 @@ func (o *OKGroup) CancelOrder(orderCancellation *order.Cancel) (err error) { if err != nil { return } + + fpair, err := o.FormatExchangeCurrency(orderCancellation.Pair, + orderCancellation.AssetType) + if err != nil { + return + } + orderCancellationResponse, err := o.CancelSpotOrder(CancelSpotOrderRequest{ - InstrumentID: o.FormatExchangeCurrency(orderCancellation.Pair, - asset.Spot).String(), - OrderID: orderID, + InstrumentID: fpair.String(), + OrderID: orderID, }) + if !orderCancellationResponse.Result { err = fmt.Errorf("order %d failed to be cancelled", orderCancellationResponse.OrderID) @@ -324,26 +332,32 @@ func (o *OKGroup) CancelOrder(orderCancellation *order.Cancel) (err error) { } // CancelAllOrders cancels all orders associated with a currency pair -func (o *OKGroup) CancelAllOrders(orderCancellation *order.Cancel) (resp order.CancelAllResponse, err error) { +func (o *OKGroup) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) { orderIDs := strings.Split(orderCancellation.ID, ",") + resp := order.CancelAllResponse{} resp.Status = make(map[string]string) var orderIDNumbers []int64 for i := range orderIDs { - orderIDNumber, strConvErr := strconv.ParseInt(orderIDs[i], 10, 64) - if strConvErr != nil { - resp.Status[orderIDs[i]] = strConvErr.Error() + orderIDNumber, err := strconv.ParseInt(orderIDs[i], 10, 64) + if err != nil { + resp.Status[orderIDs[i]] = err.Error() continue } orderIDNumbers = append(orderIDNumbers, orderIDNumber) } + fpair, err := o.FormatExchangeCurrency(orderCancellation.Pair, + orderCancellation.AssetType) + if err != nil { + return resp, err + } + cancelOrdersResponse, err := o.CancelMultipleSpotOrders(CancelMultipleSpotOrdersRequest{ - InstrumentID: o.FormatExchangeCurrency(orderCancellation.Pair, - asset.Spot).String(), - OrderIDs: orderIDNumbers, + InstrumentID: fpair.String(), + OrderIDs: orderIDNumbers, }) if err != nil { - return + return resp, err } for x := range cancelOrdersResponse { @@ -352,7 +366,7 @@ func (o *OKGroup) CancelAllOrders(orderCancellation *order.Cancel) (resp order.C } } - return + return resp, err } // GetOrderInfo returns information on a current open order @@ -361,10 +375,20 @@ func (o *OKGroup) GetOrderInfo(orderID string) (resp order.Detail, err error) { if err != nil { return } + + format, err := o.GetPairFormat(asset.Spot, false) + if err != nil { + return resp, err + } + + p, err := currency.NewPairDelimiter(mOrder.InstrumentID, format.Delimiter) + if err != nil { + return resp, err + } + resp = order.Detail{ - Amount: mOrder.Size, - Pair: currency.NewPairDelimiter(mOrder.InstrumentID, - o.GetPairFormat(asset.Spot, false).Delimiter), + Amount: mOrder.Size, + Pair: p, Exchange: o.Name, Date: mOrder.Timestamp, ExecutedAmount: mOrder.FilledSize, @@ -424,9 +448,12 @@ func (o *OKGroup) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw // GetActiveOrders retrieves any orders that are active/open func (o *OKGroup) GetActiveOrders(req *order.GetOrdersRequest) (resp []order.Detail, err error) { for x := range req.Pairs { + fpair, err := o.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } spotOpenOrders, err := o.GetSpotOpenOrders(GetSpotOpenOrdersRequest{ - InstrumentID: o.FormatExchangeCurrency(req.Pairs[x], - asset.Spot).String(), + InstrumentID: fpair.String(), }) if err != nil { return resp, err @@ -454,10 +481,13 @@ func (o *OKGroup) GetActiveOrders(req *order.GetOrdersRequest) (resp []order.Det // Can Limit response to specific order status func (o *OKGroup) GetOrderHistory(req *order.GetOrdersRequest) (resp []order.Detail, err error) { for x := range req.Pairs { + fpair, err := o.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } spotOpenOrders, err := o.GetSpotOrders(GetSpotOrdersRequest{ - Status: strings.Join([]string{"filled", "cancelled", "failure"}, "|"), - InstrumentID: o.FormatExchangeCurrency(req.Pairs[x], - asset.Spot).String(), + Status: strings.Join([]string{"filled", "cancelled", "failure"}, "|"), + InstrumentID: fpair.String(), }) if err != nil { return resp, err @@ -481,11 +511,6 @@ func (o *OKGroup) GetOrderHistory(req *order.GetOrdersRequest) (resp []order.Det return } -// GetWebsocket returns a pointer to the exchange websocket -func (o *OKGroup) GetWebsocket() (*wshandler.Websocket, error) { - return o.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (o *OKGroup) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !o.AllowAuthenticatedRequest() && // Todo check connection status @@ -500,25 +525,6 @@ func (o *OKGroup) GetWithdrawCapabilities() uint32 { return o.GetWithdrawPermissions() } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (o *OKGroup) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - o.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (o *OKGroup) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - o.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (o *OKGroup) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return o.Websocket.GetSubscriptions(), nil -} - // AuthenticateWebsocket sends an authentication message to the websocket func (o *OKGroup) AuthenticateWebsocket() error { return o.WsLogin() diff --git a/exchanges/order/order_test.go b/exchanges/order/order_test.go index 3e83148f..35182379 100644 --- a/exchanges/order/order_test.go +++ b/exchanges/order/order_test.go @@ -12,72 +12,67 @@ import ( func TestValidate(t *testing.T) { testPair := currency.NewPair(currency.BTC, currency.LTC) tester := []struct { - Pair currency.Pair - Side - Type - Amount float64 - Price float64 ExpectedErr error + Submit *Submit }{ + { + ExpectedErr: ErrSubmissionIsNil, + Submit: nil, + }, // nil struct { ExpectedErr: ErrPairIsEmpty, + Submit: &Submit{}, }, // empty pair { - Pair: testPair, + ExpectedErr: ErrSideIsInvalid, + Submit: &Submit{Pair: testPair}, }, // valid pair but invalid order side { - Pair: testPair, - Side: Buy, ExpectedErr: ErrTypeIsInvalid, + Submit: &Submit{Pair: testPair, + Side: Buy}, }, // valid pair and order side but invalid order type { - Pair: testPair, - Side: Sell, ExpectedErr: ErrTypeIsInvalid, + Submit: &Submit{Pair: testPair, + Side: Sell}, }, // valid pair and order side but invalid order type { - Pair: testPair, - Side: Bid, ExpectedErr: ErrTypeIsInvalid, + Submit: &Submit{Pair: testPair, + Side: Bid}, }, // valid pair and order side but invalid order type { - Pair: testPair, - Side: Ask, ExpectedErr: ErrTypeIsInvalid, + Submit: &Submit{Pair: testPair, + Side: Ask}, }, // valid pair and order side but invalid order type { - Pair: testPair, - Side: Ask, - Type: Market, ExpectedErr: ErrAmountIsInvalid, + Submit: &Submit{Pair: testPair, + Side: Ask, + Type: Market}, }, // valid pair, order side, type but invalid amount { - Pair: testPair, - Side: Ask, - Type: Limit, - Amount: 1, ExpectedErr: ErrPriceMustBeSetIfLimitOrder, + Submit: &Submit{Pair: testPair, + Side: Ask, + Type: Limit, + Amount: 1}, }, // valid pair, order side, type, amount but invalid price { - Pair: testPair, - Side: Ask, - Type: Limit, - Amount: 1, - Price: 1000, ExpectedErr: nil, + Submit: &Submit{Pair: testPair, + Side: Ask, + Type: Limit, + Amount: 1, + Price: 1000}, }, // valid order! } for x := range tester { - s := Submit{ - Pair: tester[x].Pair, - Side: tester[x].Side, - Type: tester[x].Type, - Amount: tester[x].Amount, - Price: tester[x].Price, - } - if err := s.Validate(); err != tester[x].ExpectedErr { + if err := tester[x].Submit.Validate(); err != tester[x].ExpectedErr { t.Errorf("Unexpected result. Got: %s, want: %s", err, tester[x].ExpectedErr) } } @@ -465,10 +460,16 @@ var stringsToOrderType = []struct { {"stop", Stop, nil}, {"STOP", Stop, nil}, {"sToP", Stop, nil}, + {"sToP LiMit", StopLimit, nil}, + {"ExchangE sToP Limit", StopLimit, nil}, {"trailing_stop", TrailingStop, nil}, {"TRAILING_STOP", TrailingStop, nil}, {"tRaIlInG_sToP", TrailingStop, nil}, {"tRaIlInG sToP", TrailingStop, nil}, + {"fOk", FillOrKill, nil}, + {"exchange fOk", FillOrKill, nil}, + {"ios", IOS, nil}, + {"post_ONly", PostOnly, nil}, {"any", AnyType, nil}, {"ANY", AnyType, nil}, {"aNy", AnyType, nil}, @@ -532,6 +533,9 @@ var stringsToOrderStatus = []struct { {"insufficient_balance", InsufficientBalance, nil}, {"INSUFFICIENT_BALANCE", InsufficientBalance, nil}, {"iNsUfFiCiEnT_bAlAnCe", InsufficientBalance, nil}, + {"PARTIALLY_CANCELLEd", PartiallyCancelled, nil}, + {"partially canceLLed", PartiallyCancelled, nil}, + {"opeN", Open, nil}, {"woahMan", UnknownStatus, errors.New("woahMan not recognised as order status")}, } @@ -583,6 +587,12 @@ func TestUpdateOrderFromModify(t *testing.T) { Trades: nil, } updated := time.Now() + + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + om := Modify{ ImmediateOrCancel: true, HiddenOrder: true, @@ -609,7 +619,7 @@ func TestUpdateOrderFromModify(t *testing.T) { Status: "1", AssetType: "1", LastUpdated: updated, - Pair: currency.NewPairFromString("BTCUSD"), + Pair: pair, Trades: []TradeHistory{}, } @@ -769,6 +779,12 @@ func TestUpdateOrderFromDetail(t *testing.T) { Trades: nil, } updated := time.Now() + + pair, err := currency.NewPairFromString("BTCUSD") + if err != nil { + t.Fatal(err) + } + om := Detail{ ImmediateOrCancel: true, HiddenOrder: true, @@ -795,7 +811,7 @@ func TestUpdateOrderFromDetail(t *testing.T) { Status: "1", AssetType: "1", LastUpdated: updated, - Pair: currency.NewPairFromString("BTCUSD"), + Pair: pair, Trades: []TradeHistory{}, } @@ -922,3 +938,14 @@ func TestUpdateOrderFromDetail(t *testing.T) { t.Error("Failed to update trades") } } + +func TestClassificationError_Error(t *testing.T) { + class := ClassificationError{OrderID: "1337", Exchange: "test", Err: errors.New("test error")} + if class.Error() != "test - OrderID: 1337 classification error: test error" { + t.Fatal("unexpected output") + } + class.OrderID = "" + if class.Error() != "test - classification error: test error" { + t.Fatal("unexpected output") + } +} diff --git a/exchanges/order/order_types.go b/exchanges/order/order_types.go index 3c443205..e70b78db 100644 --- a/exchanges/order/order_types.go +++ b/exchanges/order/order_types.go @@ -2,7 +2,6 @@ package order import ( "errors" - "fmt" "time" "github.com/thrasher-corp/gocryptotrader/currency" @@ -222,9 +221,13 @@ const ( AnyType Type = "ANY" Limit Type = "LIMIT" Market Type = "MARKET" + PostOnly Type = "POST_ONLY" ImmediateOrCancel Type = "IMMEDIATE_OR_CANCEL" Stop Type = "STOP" + StopLimit Type = "STOP LIMIT" TrailingStop Type = "TRAILING_STOP" + FillOrKill Type = "FOK" + IOS Type = "IOS" UnknownType Type = "UNKNOWN" ) @@ -263,15 +266,3 @@ type ClassificationError struct { OrderID string Err error } - -func (o *ClassificationError) Error() string { - if o.OrderID != "" { - return fmt.Sprintf("%s - OrderID: %s classification error: %v", - o.Exchange, - o.OrderID, - o.Err) - } - return fmt.Sprintf("%s - classification error: %v", - o.Exchange, - o.Err) -} diff --git a/exchanges/order/orders.go b/exchanges/order/orders.go index cab3812f..44154981 100644 --- a/exchanges/order/orders.go +++ b/exchanges/order/orders.go @@ -2,6 +2,7 @@ package order import ( "errors" + "fmt" "sort" "strings" "time" @@ -582,20 +583,36 @@ func StringToOrderSide(side string) (Side, error) { // and returning a real Type func StringToOrderType(oType string) (Type, error) { switch { - case strings.EqualFold(oType, Limit.String()): + case strings.EqualFold(oType, Limit.String()), + strings.EqualFold(oType, "EXCHANGE LIMIT"): return Limit, nil - case strings.EqualFold(oType, Market.String()): + case strings.EqualFold(oType, Market.String()), + strings.EqualFold(oType, "EXCHANGE MARKET"): return Market, nil case strings.EqualFold(oType, ImmediateOrCancel.String()), - strings.EqualFold(oType, "immediate or cancel"): + strings.EqualFold(oType, "immediate or cancel"), + strings.EqualFold(oType, "IOC"), + strings.EqualFold(oType, "EXCHANGE IOC"): return ImmediateOrCancel, nil case strings.EqualFold(oType, Stop.String()), strings.EqualFold(oType, "stop loss"), - strings.EqualFold(oType, "stop_loss"): + strings.EqualFold(oType, "stop_loss"), + strings.EqualFold(oType, "EXCHANGE STOP"): return Stop, nil + case strings.EqualFold(oType, StopLimit.String()), + strings.EqualFold(oType, "EXCHANGE STOP LIMIT"): + return StopLimit, nil case strings.EqualFold(oType, TrailingStop.String()), - strings.EqualFold(oType, "trailing stop"): + strings.EqualFold(oType, "trailing stop"), + strings.EqualFold(oType, "EXCHANGE TRAILING STOP"): return TrailingStop, nil + case strings.EqualFold(oType, FillOrKill.String()), + strings.EqualFold(oType, "EXCHANGE FOK"): + return FillOrKill, nil + case strings.EqualFold(oType, IOS.String()): + return IOS, nil + case strings.EqualFold(oType, PostOnly.String()): + return PostOnly, nil case strings.EqualFold(oType, AnyType.String()): return AnyType, nil default: @@ -647,3 +664,15 @@ func StringToOrderStatus(status string) (Status, error) { return UnknownStatus, errors.New(status + " not recognised as order status") } } + +func (o *ClassificationError) Error() string { + if o.OrderID != "" { + return fmt.Sprintf("%s - OrderID: %s classification error: %v", + o.Exchange, + o.OrderID, + o.Err) + } + return fmt.Sprintf("%s - classification error: %v", + o.Exchange, + o.Err) +} diff --git a/exchanges/orderbook/orderbook.go b/exchanges/orderbook/orderbook.go index aad84f22..66acf0f9 100644 --- a/exchanges/orderbook/orderbook.go +++ b/exchanges/orderbook/orderbook.go @@ -31,12 +31,12 @@ func SubscribeOrderbook(exchange string, p currency.Pair, a asset.Item) (dispatc defer service.RUnlock() book, ok := service.Books[exchange][p.Base.Item][p.Quote.Item][a] if !ok { - return dispatch.Pipe{}, fmt.Errorf("orderbook item not found for %s %s %s", - exchange, - p, - a) + return dispatch.Pipe{}, + fmt.Errorf("orderbook item not found for %s %s %s", + exchange, + p, + a) } - return service.mux.Subscribe(book.Main) } @@ -50,65 +50,46 @@ func SubscribeToExchangeOrderbooks(exchange string) (dispatch.Pipe, error) { return dispatch.Pipe{}, fmt.Errorf("%s exchange orderbooks not found", exchange) } - return service.mux.Subscribe(id) } // Update stores orderbook data func (s *Service) Update(b *Base) error { - var ids []uuid.UUID - + name := strings.ToLower(b.ExchangeName) s.Lock() - switch { - case s.Books[b.ExchangeName] == nil: - s.Books[b.ExchangeName] = make(map[*currency.Item]map[*currency.Item]map[asset.Item]*Book) - s.Books[b.ExchangeName][b.Pair.Base.Item] = make(map[*currency.Item]map[asset.Item]*Book) - s.Books[b.ExchangeName][b.Pair.Base.Item][b.Pair.Quote.Item] = make(map[asset.Item]*Book) - err := s.SetNewData(b) - if err != nil { - s.Unlock() - return err - } - - case s.Books[b.ExchangeName][b.Pair.Base.Item] == nil: - s.Books[b.ExchangeName][b.Pair.Base.Item] = make(map[*currency.Item]map[asset.Item]*Book) - s.Books[b.ExchangeName][b.Pair.Base.Item][b.Pair.Quote.Item] = make(map[asset.Item]*Book) - err := s.SetNewData(b) - if err != nil { - s.Unlock() - return err - } - - case s.Books[b.ExchangeName][b.Pair.Base.Item][b.Pair.Quote.Item] == nil: - s.Books[b.ExchangeName][b.Pair.Base.Item][b.Pair.Quote.Item] = make(map[asset.Item]*Book) - err := s.SetNewData(b) - if err != nil { - s.Unlock() - return err - } - - case s.Books[b.ExchangeName][b.Pair.Base.Item][b.Pair.Quote.Item][b.AssetType] == nil: - err := s.SetNewData(b) - if err != nil { - s.Unlock() - return err - } - - default: - book := s.Books[b.ExchangeName][b.Pair.Base.Item][b.Pair.Quote.Item][b.AssetType] + book, ok := s.Books[name][b.Pair.Base.Item][b.Pair.Quote.Item][b.AssetType] + if ok { book.b.Bids = b.Bids book.b.Asks = b.Asks book.b.LastUpdated = b.LastUpdated - ids = book.Assoc - ids = append(ids, book.Main) + ids := append(book.Assoc, book.Main) + s.Unlock() + return s.mux.Publish(ids, b) + } + + switch { + case s.Books[name] == nil: + s.Books[name] = make(map[*currency.Item]map[*currency.Item]map[asset.Item]*Book) + fallthrough + case s.Books[name][b.Pair.Base.Item] == nil: + s.Books[name][b.Pair.Base.Item] = make(map[*currency.Item]map[asset.Item]*Book) + fallthrough + case s.Books[name][b.Pair.Base.Item][b.Pair.Quote.Item] == nil: + s.Books[name][b.Pair.Base.Item][b.Pair.Quote.Item] = make(map[asset.Item]*Book) + } + + err := s.SetNewData(b, name) + if err != nil { + s.Unlock() + return err } s.Unlock() - return s.mux.Publish(ids, b) + return nil } // SetNewData sets new data -func (s *Service) SetNewData(b *Base) error { - ids, err := s.GetAssociations(b) +func (s *Service) SetNewData(b *Base, fmtName string) error { + ids, err := s.GetAssociations(b, fmtName) if err != nil { return err } @@ -126,7 +107,7 @@ func (s *Service) SetNewData(b *Base) error { cpyBook.Asks = make([]Item, len(b.Asks)) copy(cpyBook.Asks, b.Asks) - s.Books[b.ExchangeName][b.Pair.Base.Item][b.Pair.Quote.Item][b.AssetType] = &Book{ + s.Books[fmtName][b.Pair.Base.Item][b.Pair.Quote.Item][b.AssetType] = &Book{ b: &cpyBook, Main: singleID, Assoc: ids} @@ -134,20 +115,20 @@ func (s *Service) SetNewData(b *Base) error { } // GetAssociations links a singular book with it's dispatch associations -func (s *Service) GetAssociations(b *Base) ([]uuid.UUID, error) { +func (s *Service) GetAssociations(b *Base, fmtName string) ([]uuid.UUID, error) { if b == nil { return nil, errors.New("orderbook is nil") } var ids []uuid.UUID - exchangeID, ok := s.Exchange[b.ExchangeName] + exchangeID, ok := s.Exchange[fmtName] if !ok { var err error exchangeID, err = s.mux.GetID() if err != nil { return nil, err } - s.Exchange[b.ExchangeName] = exchangeID + s.Exchange[fmtName] = exchangeID } ids = append(ids, exchangeID) @@ -247,8 +228,6 @@ func (b *Base) Process() error { return errors.New(errExchangeNameUnset) } - b.ExchangeName = strings.ToLower(b.ExchangeName) - if b.Pair.IsEmpty() { return errors.New(errPairNotSet) } diff --git a/exchanges/orderbook/orderbook_test.go b/exchanges/orderbook/orderbook_test.go index 8915da03..01e8ad97 100644 --- a/exchanges/orderbook/orderbook_test.go +++ b/exchanges/orderbook/orderbook_test.go @@ -125,7 +125,7 @@ func TestSubscribeToExchangeOrderbooks(t *testing.T) { err = b.Process() if err != nil { - t.Error("", err) + t.Error(err) } _, err = SubscribeToExchangeOrderbooks("SubscribeToExchangeOrderbooks") @@ -158,7 +158,10 @@ func TestVerify(t *testing.T) { func TestCalculateTotalBids(t *testing.T) { t.Parallel() - curr := currency.NewPairFromStrings("BTC", "USD") + curr, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } base := Base{ Pair: curr, Bids: []Item{{Price: 100, Amount: 10}}, @@ -173,7 +176,10 @@ func TestCalculateTotalBids(t *testing.T) { func TestCalculateTotaAsks(t *testing.T) { t.Parallel() - curr := currency.NewPairFromStrings("BTC", "USD") + curr, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } base := Base{ Pair: curr, Asks: []Item{{Price: 100, Amount: 10}}, @@ -187,7 +193,10 @@ func TestCalculateTotaAsks(t *testing.T) { func TestUpdate(t *testing.T) { t.Parallel() - curr := currency.NewPairFromStrings("BTC", "USD") + curr, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } timeNow := time.Now() base := Base{ Pair: curr, @@ -217,7 +226,10 @@ func TestUpdate(t *testing.T) { } func TestGetOrderbook(t *testing.T) { - c := currency.NewPairFromStrings("BTC", "USD") + c, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } base := &Base{ Pair: c, Asks: []Item{{Price: 100, Amount: 10}}, @@ -226,7 +238,7 @@ func TestGetOrderbook(t *testing.T) { AssetType: asset.Spot, } - err := base.Process() + err = base.Process() if err != nil { t.Fatal(err) } @@ -251,7 +263,10 @@ func TestGetOrderbook(t *testing.T) { t.Fatal("TestGetOrderbook retrieved non-existent orderbook using invalid first currency") } - newCurrency := currency.NewPairFromStrings("BTC", "AUD") + newCurrency, err := currency.NewPairFromStrings("BTC", "AUD") + if err != nil { + t.Fatal(err) + } _, err = Get("Exchange", newCurrency, asset.Spot) if err == nil { t.Fatal("TestGetOrderbook retrieved non-existent orderbook using invalid second currency") @@ -270,7 +285,10 @@ func TestGetOrderbook(t *testing.T) { } func TestCreateNewOrderbook(t *testing.T) { - c := currency.NewPairFromStrings("BTC", "USD") + c, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } base := &Base{ Pair: c, Asks: []Item{{Price: 100, Amount: 10}}, @@ -279,7 +297,7 @@ func TestCreateNewOrderbook(t *testing.T) { AssetType: asset.Spot, } - err := base.Process() + err = base.Process() if err != nil { t.Fatal(err) } @@ -305,7 +323,10 @@ func TestCreateNewOrderbook(t *testing.T) { } func TestProcessOrderbook(t *testing.T) { - c := currency.NewPairFromStrings("BTC", "USD") + c, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } base := Base{ Asks: []Item{{Price: 100, Amount: 10}}, Bids: []Item{{Price: 200, Amount: 10}}, @@ -314,7 +335,7 @@ func TestProcessOrderbook(t *testing.T) { // test for empty pair base.Pair = currency.Pair{} - err := base.Process() + err = base.Process() if err == nil { t.Error("empty pair should throw an err") } @@ -341,7 +362,10 @@ func TestProcessOrderbook(t *testing.T) { } // now test for processing a pair with a different quote currency - c = currency.NewPairFromStrings("BTC", "GBP") + c, err = currency.NewPairFromStrings("BTC", "GBP") + if err != nil { + t.Fatal(err) + } base.Pair = c err = base.Process() if err != nil { @@ -356,7 +380,10 @@ func TestProcessOrderbook(t *testing.T) { } // now test for processing a pair which has a different base currency - c = currency.NewPairFromStrings("LTC", "GBP") + c, err = currency.NewPairFromStrings("LTC", "GBP") + if err != nil { + t.Fatal(err) + } base.Pair = c err = base.Process() if err != nil { @@ -491,14 +518,14 @@ func TestProcessOrderbook(t *testing.T) { } func TestSetNewData(t *testing.T) { - err := service.SetNewData(nil) + err := service.SetNewData(nil, "") if err == nil { t.Error("error cannot be nil") } } func TestGetAssociations(t *testing.T) { - _, err := service.GetAssociations(nil) + _, err := service.GetAssociations(nil, "") if err == nil { t.Error("error cannot be nil") } diff --git a/exchanges/poloniex/poloniex.go b/exchanges/poloniex/poloniex.go index f1205e96..618f31cd 100644 --- a/exchanges/poloniex/poloniex.go +++ b/exchanges/poloniex/poloniex.go @@ -16,7 +16,6 @@ import ( exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) const ( @@ -54,7 +53,6 @@ const ( // Poloniex is the overarching type across the poloniex package type Poloniex struct { exchange.Base - WebsocketConn *wshandler.WebsocketConnection } // GetTicker returns current ticker information diff --git a/exchanges/poloniex/poloniex_live_test.go b/exchanges/poloniex/poloniex_live_test.go index 36c5afee..8eabea2a 100644 --- a/exchanges/poloniex/poloniex_live_test.go +++ b/exchanges/poloniex/poloniex_live_test.go @@ -29,6 +29,7 @@ func TestMain(m *testing.M) { poloniexConfig.API.Credentials.Key = apiKey poloniexConfig.API.Credentials.Secret = apiSecret p.SetDefaults() + p.Websocket = sharedtestvalues.NewTestWebsocket() err = p.Setup(poloniexConfig) if err != nil { log.Fatal("Poloniex setup error", err) diff --git a/exchanges/poloniex/poloniex_mock_test.go b/exchanges/poloniex/poloniex_mock_test.go index 0df790ae..ad2cecdd 100644 --- a/exchanges/poloniex/poloniex_mock_test.go +++ b/exchanges/poloniex/poloniex_mock_test.go @@ -33,6 +33,7 @@ func TestMain(m *testing.M) { poloniexConfig.API.Credentials.Key = apiKey poloniexConfig.API.Credentials.Secret = apiSecret p.SetDefaults() + p.Websocket = sharedtestvalues.NewTestWebsocket() err = p.Setup(poloniexConfig) if err != nil { log.Fatal("Poloniex setup error", err) @@ -45,8 +46,6 @@ func TestMain(m *testing.M) { p.HTTPClient = newClient p.API.Endpoints.URL = serverDetails - p.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - p.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() log.Printf(sharedtestvalues.MockTesting, p.Name, p.API.Endpoints.URL) os.Exit(m.Run()) } diff --git a/exchanges/poloniex/poloniex_test.go b/exchanges/poloniex/poloniex_test.go index 700a1ba1..25e74ee4 100644 --- a/exchanges/poloniex/poloniex_test.go +++ b/exchanges/poloniex/poloniex_test.go @@ -14,7 +14,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -259,7 +259,7 @@ func TestSubmitOrder(t *testing.T) { var orderSubmission = &order.Submit{ Pair: currency.Pair{ - Delimiter: delimiterUnderscore, + Delimiter: currency.UnderscoreDelimiter, Base: currency.BTC, Quote: currency.LTC, }, @@ -422,22 +422,13 @@ func TestGetDepositAddress(t *testing.T) { func TestWsAuth(t *testing.T) { t.Parallel() if !p.Websocket.IsEnabled() && !p.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { - t.Skip(wshandler.WebsocketNotEnabled) - } - p.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: p.Name, - URL: p.Websocket.GetWebsocketURL(), - Verbose: p.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + t.Skip(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := p.WebsocketConn.Dial(&dialer, http.Header{}) + err := p.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } - p.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - p.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() go p.wsReadData() err = p.wsSendAuthorisedCommand("subscribe") if err != nil { @@ -534,8 +525,11 @@ func TestWsHandleAccountData(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCLTC") - _, err := p.GetHistoricCandles(currencyPair, asset.Spot, time.Unix(1588741402, 0), time.Unix(1588745003, 0), kline.FiveMin) + currencyPair, err := currency.NewPairFromString("BTCLTC") + if err != nil { + t.Fatal(err) + } + _, err = p.GetHistoricCandles(currencyPair, asset.Spot, time.Unix(1588741402, 0), time.Unix(1588745003, 0), kline.FiveMin) if err != nil { t.Fatal(err) } @@ -552,8 +546,11 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("BTCLTC") - _, err := p.GetHistoricCandlesExtended(currencyPair, asset.Spot, time.Unix(1588741402, 0), time.Unix(1588745003, 0), kline.FiveMin) + currencyPair, err := currency.NewPairFromString("BTCLTC") + if err != nil { + t.Fatal(err) + } + _, err = p.GetHistoricCandlesExtended(currencyPair, asset.Spot, time.Unix(1588741402, 0), time.Unix(1588745003, 0), kline.FiveMin) if err != nil { t.Fatal(err) } diff --git a/exchanges/poloniex/poloniex_websocket.go b/exchanges/poloniex/poloniex_websocket.go index d671f71f..d02a2f5e 100644 --- a/exchanges/poloniex/poloniex_websocket.go +++ b/exchanges/poloniex/poloniex_websocket.go @@ -17,9 +17,9 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" ) const ( @@ -28,7 +28,6 @@ const ( wsTickerDataID = 1002 ws24HourExchangeVolumeID = 1003 wsHeartbeat = 1010 - delimiterUnderscore = "_" ) var ( @@ -39,23 +38,26 @@ var ( // WsConnect initiates a websocket connection func (p *Poloniex) WsConnect() error { if !p.Websocket.IsEnabled() || !p.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := p.WebsocketConn.Dial(&dialer, http.Header{}) + err := p.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } - err2 := p.getCurrencyIDMap() - if err2 != nil { - return err2 + err = p.getCurrencyIDMap() + if err != nil { + return err } go p.wsReadData() - p.GenerateDefaultSubscriptions() + subs, err := p.GenerateDefaultSubscriptions() + if err != nil { + return err + } - return nil + return p.Websocket.SubscribeToChannels(subs) } func (p *Poloniex) getCurrencyIDMap() error { @@ -86,26 +88,16 @@ func checkSubscriptionSuccess(data []interface{}) bool { // wsReadData handles data from the websocket connection func (p *Poloniex) wsReadData() { p.Websocket.Wg.Add(1) - - defer func() { - p.Websocket.Wg.Done() - }() + defer p.Websocket.Wg.Done() for { - select { - case <-p.Websocket.ShutdownC: + resp := p.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := p.WebsocketConn.ReadMessage() - if err != nil { - p.Websocket.ReadMessageErrors <- err - return - } - p.Websocket.TrafficAlert <- struct{}{} - err = p.wsHandleData(resp.Raw) - if err != nil { - p.Websocket.DataHandler <- err - } + } + err := p.wsHandleData(resp.Raw) + if err != nil { + p.Websocket.DataHandler <- err } } } @@ -189,14 +181,20 @@ func (p *Poloniex) wsHandleData(respRaw []byte) error { } var currPair currency.Pair if currPairFromMap, ok := currencyIDMap[notification[1].(float64)]; ok { - currPair = currency.NewPairFromString(currPairFromMap) + currPair, err = currency.NewPairFromString(currPairFromMap) + if err != nil { + return err + } } else { // It is better to still log an order which you can recheck later, rather than error out p.Websocket.DataHandler <- fmt.Errorf(p.Name+ " - Unknown currency pair ID. "+ "Currency will appear as the pair ID: '%v'", notification[1].(float64)) - currPair = currency.NewPairFromString(strconv.FormatFloat(notification[1].(float64), 'f', -1, 64)) + currPair, err = currency.NewPairFromString(strconv.FormatFloat(notification[1].(float64), 'f', -1, 64)) + if err != nil { + return err + } } var a asset.Item a, err = p.GetPairAssetType(currPair) @@ -303,12 +301,6 @@ func (p *Poloniex) wsHandleData(respRaw []byte) error { if err != nil { return err } - - p.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: p.Name, - Asset: asset.Spot, - Pair: currency.NewPairFromString(currencyPair), - } case "o": currencyPair := currencyIDMap[channelID] dataL3 := dataL2.([]interface{}) @@ -318,12 +310,6 @@ func (p *Poloniex) wsHandleData(respRaw []byte) error { if err != nil { return err } - - p.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Exchange: p.Name, - Asset: asset.Spot, - Pair: currency.NewPairFromString(currencyPair), - } case "t": currencyPair := currencyIDMap[channelID] var trade WsTrade @@ -347,15 +333,20 @@ func (p *Poloniex) wsHandleData(respRaw []byte) error { } trade.Timestamp = int64(dataL3[5].(float64)) - p.Websocket.DataHandler <- wshandler.TradeData{ + pair, err := currency.NewPairFromString(currencyPair) + if err != nil { + return err + } + + p.Websocket.DataHandler <- stream.TradeData{ Timestamp: time.Unix(trade.Timestamp, 0), - CurrencyPair: currency.NewPairFromString(currencyPair), + CurrencyPair: pair, Side: side, Amount: trade.Volume, Price: trade.Price, } default: - p.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: p.Name + wshandler.UnhandledMessage + string(respRaw)} + p.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: p.Name + stream.UnhandledMessage + string(respRaw)} return nil } } @@ -368,14 +359,31 @@ func (p *Poloniex) wsHandleData(respRaw []byte) error { func (p *Poloniex) wsHandleTickerData(data []interface{}) error { tickerData := data[2].([]interface{}) var t WsTicker - currencyPair := currency.NewPairDelimiter(currencyIDMap[tickerData[0].(float64)], delimiterUnderscore) - if !p.GetEnabledPairs(asset.Spot).Contains(currencyPair, true) { - // Ticker subscription receives all currencies, no specific subscription - // There should be no error associated with receiving data of disabled currency ticker data + currencyPair, err := currency.NewPairDelimiter(currencyIDMap[tickerData[0].(float64)], + currency.UnderscoreDelimiter) + if err != nil { + return err + } + + enabled, err := p.GetEnabledPairs(asset.Spot) + if err != nil { + return err + } + + if !enabled.Contains(currencyPair, true) { + var avail currency.Pairs + avail, err = p.GetAvailablePairs(asset.Spot) + if err != nil { + return err + } + + if !avail.Contains(currencyPair, true) { + return fmt.Errorf("currency pair %s not found in available pair list", + currencyPair) + } return nil } - var err error t.LastPrice, err = strconv.ParseFloat(tickerData[1].(string), 64) if err != nil { return err @@ -477,7 +485,12 @@ func (p *Poloniex) WsProcessOrderbookSnapshot(ob []interface{}, symbol string) e newOrderBook.Asks = asks newOrderBook.Bids = bids newOrderBook.AssetType = asset.Spot - newOrderBook.Pair = currency.NewPairFromString(symbol) + + var err error + newOrderBook.Pair, err = currency.NewPairFromString(symbol) + if err != nil { + return err + } newOrderBook.ExchangeName = p.Name return p.Websocket.Orderbook.LoadSnapshot(&newOrderBook) @@ -485,7 +498,10 @@ func (p *Poloniex) WsProcessOrderbookSnapshot(ob []interface{}, symbol string) e // WsProcessOrderbookUpdate processes new orderbook updates func (p *Poloniex) WsProcessOrderbookUpdate(sequenceNumber int64, target []interface{}, symbol string) error { - cP := currency.NewPairFromString(symbol) + cP, err := currency.NewPairFromString(symbol) + if err != nil { + return err + } price, err := strconv.ParseFloat(target[2].(string), 64) if err != nil { return err @@ -494,7 +510,7 @@ func (p *Poloniex) WsProcessOrderbookUpdate(sequenceNumber int64, target []inter if err != nil { return err } - update := &wsorderbook.WebsocketOrderbookUpdate{ + update := &buffer.Update{ Pair: cP, Asset: asset.Spot, UpdateID: sequenceNumber, @@ -508,64 +524,114 @@ func (p *Poloniex) WsProcessOrderbookUpdate(sequenceNumber int64, target []inter } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (p *Poloniex) GenerateDefaultSubscriptions() { - var subscriptions []wshandler.WebsocketChannelSubscription - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ +func (p *Poloniex) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: strconv.FormatInt(wsTickerDataID, 10), }) if p.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: strconv.FormatInt(wsAccountNotificationID, 10), }) } - enabledCurrencies := p.GetEnabledPairs(asset.Spot) + enabledCurrencies, err := p.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } for j := range enabledCurrencies { - enabledCurrencies[j].Delimiter = delimiterUnderscore - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + enabledCurrencies[j].Delimiter = currency.UnderscoreDelimiter + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: "orderbook", Currency: enabledCurrencies[j], + Asset: asset.Spot, }) } - p.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (p *Poloniex) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - subscriptionRequest := WsCommand{ - Command: "subscribe", +func (p *Poloniex) Subscribe(sub []stream.ChannelSubscription) error { + var errs common.Errors +channels: + for i := range sub { + subscriptionRequest := WsCommand{ + Command: "subscribe", + } + switch { + case strings.EqualFold(strconv.FormatInt(wsAccountNotificationID, 10), + sub[i].Channel): + err := p.wsSendAuthorisedCommand("subscribe") + if err != nil { + errs = append(errs, err) + continue channels + } + p.Websocket.AddSuccessfulSubscriptions(sub[i]) + continue channels + case strings.EqualFold(strconv.FormatInt(wsTickerDataID, 10), + sub[i].Channel): + subscriptionRequest.Channel = wsTickerDataID + default: + subscriptionRequest.Channel = sub[i].Currency.String() + } + + err := p.Websocket.Conn.SendJSONMessage(subscriptionRequest) + if err != nil { + errs = append(errs, err) + continue + } + + p.Websocket.AddSuccessfulSubscriptions(sub[i]) } - switch { - case strings.EqualFold(strconv.FormatInt(wsAccountNotificationID, 10), channelToSubscribe.Channel): - return p.wsSendAuthorisedCommand("subscribe") - case strings.EqualFold(strconv.FormatInt(wsTickerDataID, 10), channelToSubscribe.Channel): - subscriptionRequest.Channel = wsTickerDataID - default: - subscriptionRequest.Channel = channelToSubscribe.Currency.String() + if errs != nil { + return errs } - return p.WebsocketConn.SendJSONMessage(subscriptionRequest) + return nil } // Unsubscribe sends a websocket message to stop receiving data from the channel -func (p *Poloniex) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - unsubscriptionRequest := WsCommand{ - Command: "unsubscribe", +func (p *Poloniex) Unsubscribe(unsub []stream.ChannelSubscription) error { + var errs common.Errors +channels: + for i := range unsub { + unsubscriptionRequest := WsCommand{ + Command: "unsubscribe", + } + switch { + case strings.EqualFold(strconv.FormatInt(wsAccountNotificationID, 10), + unsub[i].Channel): + err := p.wsSendAuthorisedCommand("unsubscribe") + if err != nil { + errs = append(errs, err) + continue channels + } + p.Websocket.RemoveSuccessfulUnsubscriptions(unsub[i]) + continue channels + case strings.EqualFold(strconv.FormatInt(wsTickerDataID, 10), + unsub[i].Channel): + unsubscriptionRequest.Channel = wsTickerDataID + default: + unsubscriptionRequest.Channel = unsub[i].Currency.String() + } + err := p.Websocket.Conn.SendJSONMessage(unsubscriptionRequest) + if err != nil { + errs = append(errs, err) + continue + } + p.Websocket.RemoveSuccessfulUnsubscriptions(unsub[i]) } - switch { - case strings.EqualFold(strconv.FormatInt(wsAccountNotificationID, 10), channelToSubscribe.Channel): - return p.wsSendAuthorisedCommand("unsubscribe") - case strings.EqualFold(strconv.FormatInt(wsTickerDataID, 10), channelToSubscribe.Channel): - unsubscriptionRequest.Channel = wsTickerDataID - default: - unsubscriptionRequest.Channel = channelToSubscribe.Currency.String() + if errs != nil { + return errs } - return p.WebsocketConn.SendJSONMessage(unsubscriptionRequest) + return nil } func (p *Poloniex) wsSendAuthorisedCommand(command string) error { nonce := fmt.Sprintf("nonce=%v", time.Now().UnixNano()) - hmac := crypto.GetHMAC(crypto.HashSHA512, []byte(nonce), []byte(p.API.Credentials.Secret)) + hmac := crypto.GetHMAC(crypto.HashSHA512, + []byte(nonce), + []byte(p.API.Credentials.Secret)) request := WsAuthorisationRequest{ Command: command, Channel: 1000, @@ -573,5 +639,5 @@ func (p *Poloniex) wsSendAuthorisedCommand(command string) error { Key: p.API.Credentials.Key, Payload: nonce, } - return p.WebsocketConn.SendJSONMessage(request) + return p.Websocket.Conn.SendJSONMessage(request) } diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index a8b5926e..fa6a02c4 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -18,8 +18,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -55,19 +55,19 @@ func (p *Poloniex) SetDefaults() { p.API.CredentialsValidator.RequiresKey = true p.API.CredentialsValidator.RequiresSecret = true - p.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: delimiterUnderscore, - Uppercase: true, - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: delimiterUnderscore, - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{ + Delimiter: currency.UnderscoreDelimiter, + Uppercase: true, + } + + configFmt := ¤cy.PairFormat{ + Delimiter: currency.UnderscoreDelimiter, + Uppercase: true, + } + + err := p.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } p.Features = exchange.Features{ @@ -133,7 +133,7 @@ func (p *Poloniex) SetDefaults() { p.API.Endpoints.URLDefault = poloniexAPIURL p.API.Endpoints.URL = p.API.Endpoints.URLDefault p.API.Endpoints.WebsocketURL = poloniexWebsocketAddress - p.Websocket = wshandler.New() + p.Websocket = stream.New() p.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit p.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout p.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit @@ -151,41 +151,31 @@ func (p *Poloniex) Setup(exch *config.ExchangeConfig) error { return err } - err = p.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: poloniexWebsocketAddress, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: p.WsConnect, - Subscriber: p.Subscribe, - UnSubscriber: p.Unsubscribe, - Features: &p.Features.Supports.WebsocketCapabilities, - }) + err = p.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: poloniexWebsocketAddress, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: p.WsConnect, + Subscriber: p.Subscribe, + UnSubscriber: p.Unsubscribe, + GenerateSubscriptions: p.GenerateDefaultSubscriptions, + Features: &p.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + SortBuffer: true, + SortBufferByUpdateIDs: true, + }) if err != nil { return err } - p.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: p.Name, - URL: p.Websocket.GetWebsocketURL(), - ProxyURL: p.Websocket.GetProxyAddress(), - Verbose: p.Verbose, + return p.Websocket.SetupNewConnection(stream.ConnectionSetup{ ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - - p.Websocket.Orderbook.Setup( - exch.WebsocketOrderbookBufferLimit, - false, - true, - true, - false, - exch.Name) - return nil + }) } // Start starts the Poloniex go routine @@ -200,13 +190,28 @@ func (p *Poloniex) Start(wg *sync.WaitGroup) { // Run implements the Poloniex wrapper func (p *Poloniex) Run() { if p.Verbose { - log.Debugf(log.ExchangeSys, "%s Websocket: %s (url: %s).\n", p.Name, common.IsEnabled(p.Websocket.IsEnabled()), poloniexWebsocketAddress) + log.Debugf(log.ExchangeSys, + "%s Websocket: %s (url: %s).\n", + p.Name, + common.IsEnabled(p.Websocket.IsEnabled()), + poloniexWebsocketAddress) p.PrintEnabledPairs() } forceUpdate := false - if common.StringDataCompare(p.GetAvailablePairs(asset.Spot).Strings(), "BTC_USDT") { - log.Warnf(log.ExchangeSys, "%s contains invalid pair, forcing upgrade of available currencies.\n", + + avail, err := p.GetAvailablePairs(asset.Spot) + if err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + p.Name, + err) + return + } + + if common.StringDataCompare(avail.Strings(), "BTC_USDT") { + log.Warnf(log.ExchangeSys, + "%s contains invalid pair, forcing upgrade of available currencies.\n", p.Name) forceUpdate = true } @@ -215,9 +220,12 @@ func (p *Poloniex) Run() { return } - err := p.UpdateTradablePairs(forceUpdate) + err = p.UpdateTradablePairs(forceUpdate) if err != nil { - log.Errorf(log.ExchangeSys, "%s failed to update tradable pairs. Err: %s", p.Name, err) + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + p.Name, + err) } } @@ -243,37 +251,47 @@ func (p *Poloniex) UpdateTradablePairs(forceUpgrade bool) error { if err != nil { return err } - - return p.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpgrade) + ps, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return p.UpdatePairs(ps, asset.Spot, false, forceUpgrade) } // UpdateTicker updates and returns the ticker for a currency pair func (p *Poloniex) UpdateTicker(currencyPair currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) tick, err := p.GetTicker() if err != nil { - return tickerPrice, err + return nil, err } - enabledPairs := p.GetEnabledPairs(assetType) + enabledPairs, err := p.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } for i := range enabledPairs { - var tp ticker.Price - curr := p.FormatExchangeCurrency(enabledPairs[i], assetType).String() + fpair, err := p.FormatExchangeCurrency(enabledPairs[i], assetType) + if err != nil { + return nil, err + } + curr := fpair.String() if _, ok := tick[curr]; !ok { continue } - tp.Pair = enabledPairs[i] - tp.Ask = tick[curr].LowestAsk - tp.Bid = tick[curr].HighestBid - tp.High = tick[curr].High24Hr - tp.Last = tick[curr].Last - tp.Low = tick[curr].Low24Hr - tp.Volume = tick[curr].BaseVolume - tp.QuoteVolume = tick[curr].QuoteVolume - err = ticker.ProcessTicker(p.Name, &tp, assetType) + err = ticker.ProcessTicker(&ticker.Price{ + Pair: enabledPairs[i], + Ask: tick[curr].LowestAsk, + Bid: tick[curr].HighestBid, + High: tick[curr].High24Hr, + Last: tick[curr].Last, + Low: tick[curr].Low24Hr, + Volume: tick[curr].BaseVolume, + QuoteVolume: tick[curr].QuoteVolume, + ExchangeName: p.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } return ticker.GetTicker(p.Name, currencyPair, assetType) @@ -304,9 +322,16 @@ func (p *Poloniex) UpdateOrderbook(currencyPair currency.Pair, assetType asset.I return nil, err } - enabledPairs := p.GetEnabledPairs(assetType) + enabledPairs, err := p.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } for i := range enabledPairs { - data, ok := orderbookNew.Data[p.FormatExchangeCurrency(enabledPairs[i], assetType).String()] + fpair, err := p.FormatExchangeCurrency(enabledPairs[i], assetType) + if err != nil { + return nil, err + } + data, ok := orderbookNew.Data[fpair.String()] if !ok { continue } @@ -516,11 +541,6 @@ func (p *Poloniex) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdra return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (p *Poloniex) GetWebsocket() (*wshandler.Websocket, error) { - return p.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (p *Poloniex) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if (!p.AllowAuthenticatedRequest() || p.SkipAuthCheck) && // Todo check connection status @@ -537,11 +557,18 @@ func (p *Poloniex) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, return nil, err } + format, err := p.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for key := range resp.Data { - symbol := currency.NewPairDelimiter(key, - p.GetPairFormat(asset.Spot, false).Delimiter) - + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(key, format.Delimiter) + if err != nil { + return nil, err + } for i := range resp.Data[key] { orderSide := order.Side(strings.ToUpper(resp.Data[key][i].Type)) orderDate, err := time.Parse(common.SimpleTimeFormat, resp.Data[key][i].Date) @@ -583,10 +610,18 @@ func (p *Poloniex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return nil, err } + format, err := p.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for key := range resp.Data { - symbol := currency.NewPairDelimiter(key, - p.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(key, format.Delimiter) + if err != nil { + return nil, err + } for i := range resp.Data[key] { orderSide := order.Side(strings.ToUpper(resp.Data[key][i].Type)) @@ -619,30 +654,6 @@ func (p *Poloniex) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (p *Poloniex) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - p.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (p *Poloniex) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - p.Websocket.RemoveSubscribedChannels(channels) - return nil -} - -// GetSubscriptions returns a copied list of subscriptions -func (p *Poloniex) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return p.Websocket.GetSubscriptions(), nil -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (p *Poloniex) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (p *Poloniex) ValidateCredentials() error { @@ -658,7 +669,12 @@ func (p *Poloniex) GetHistoricCandles(pair currency.Pair, a asset.Item, start, e } } - candles, err := p.GetChartData(p.FormatExchangeCurrency(pair, a).String(), + formattedPair, err := p.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + + candles, err := p.GetChartData(formattedPair.String(), start, end, p.FormatExchangeKlineInterval(interval)) if err != nil { diff --git a/exchanges/protocol/features.go b/exchanges/protocol/features.go index 821907a2..ef12bfc4 100644 --- a/exchanges/protocol/features.go +++ b/exchanges/protocol/features.go @@ -3,35 +3,38 @@ package protocol // Features holds all variables for the exchanges supported features // for a protocol (e.g REST or Websocket) type Features struct { - TickerBatching bool `json:"tickerBatching,omitempty"` - AutoPairUpdates bool `json:"autoPairUpdates,omitempty"` - AccountBalance bool `json:"accountBalance,omitempty"` - CryptoDeposit bool `json:"cryptoDeposit,omitempty"` - CryptoWithdrawal bool `json:"cryptoWithdrawal,omitempty"` - FiatWithdraw bool `json:"fiatWithdraw,omitempty"` - GetOrder bool `json:"getOrder,omitempty"` - GetOrders bool `json:"getOrders,omitempty"` - CancelOrders bool `json:"cancelOrders,omitempty"` - CancelOrder bool `json:"cancelOrder,omitempty"` - SubmitOrder bool `json:"submitOrder,omitempty"` - SubmitOrders bool `json:"submitOrders,omitempty"` - ModifyOrder bool `json:"modifyOrder,omitempty"` - DepositHistory bool `json:"depositHistory,omitempty"` - WithdrawalHistory bool `json:"withdrawalHistory,omitempty"` - TradeHistory bool `json:"tradeHistory,omitempty"` - UserTradeHistory bool `json:"userTradeHistory,omitempty"` - TradeFee bool `json:"tradeFee,omitempty"` - FiatDepositFee bool `json:"fiatDepositFee,omitempty"` - FiatWithdrawalFee bool `json:"fiatWithdrawalFee,omitempty"` - CryptoDepositFee bool `json:"cryptoDepositFee,omitempty"` - CryptoWithdrawalFee bool `json:"cryptoWithdrawalFee,omitempty"` - TickerFetching bool `json:"tickerFetching,omitempty"` - KlineFetching bool `json:"klineFetching,omitempty"` - TradeFetching bool `json:"tradeFetching,omitempty"` - OrderbookFetching bool `json:"orderbookFetching,omitempty"` - AccountInfo bool `json:"accountInfo,omitempty"` - FiatDeposit bool `json:"fiatDeposit,omitempty"` - DeadMansSwitch bool `json:"deadMansSwitch,omitempty"` + TickerBatching bool `json:"tickerBatching,omitempty"` + AutoPairUpdates bool `json:"autoPairUpdates,omitempty"` + AccountBalance bool `json:"accountBalance,omitempty"` + CryptoDeposit bool `json:"cryptoDeposit,omitempty"` + CryptoWithdrawal bool `json:"cryptoWithdrawal,omitempty"` + FiatWithdraw bool `json:"fiatWithdraw,omitempty"` + GetOrder bool `json:"getOrder,omitempty"` + GetOrders bool `json:"getOrders,omitempty"` + CancelOrders bool `json:"cancelOrders,omitempty"` + CancelOrder bool `json:"cancelOrder,omitempty"` + SubmitOrder bool `json:"submitOrder,omitempty"` + SubmitOrders bool `json:"submitOrders,omitempty"` + ModifyOrder bool `json:"modifyOrder,omitempty"` + DepositHistory bool `json:"depositHistory,omitempty"` + WithdrawalHistory bool `json:"withdrawalHistory,omitempty"` + TradeHistory bool `json:"tradeHistory,omitempty"` + UserTradeHistory bool `json:"userTradeHistory,omitempty"` + TradeFee bool `json:"tradeFee,omitempty"` + FiatDepositFee bool `json:"fiatDepositFee,omitempty"` + FiatWithdrawalFee bool `json:"fiatWithdrawalFee,omitempty"` + CryptoDepositFee bool `json:"cryptoDepositFee,omitempty"` + CryptoWithdrawalFee bool `json:"cryptoWithdrawalFee,omitempty"` + TickerFetching bool `json:"tickerFetching,omitempty"` + KlineFetching bool `json:"klineFetching,omitempty"` + TradeFetching bool `json:"tradeFetching,omitempty"` + OrderbookFetching bool `json:"orderbookFetching,omitempty"` + AccountInfo bool `json:"accountInfo,omitempty"` + FiatDeposit bool `json:"fiatDeposit,omitempty"` + DeadMansSwitch bool `json:"deadMansSwitch,omitempty"` + // FullPayloadSubscribe flushes and changes full subscription on websocket + // connection by subscribing with full default stream channel list + FullPayloadSubscribe bool `json:"fullPayloadSubscribe,omitempty"` Subscribe bool `json:"subscribe,omitempty"` Unsubscribe bool `json:"unsubscribe,omitempty"` AuthenticatedEndpoints bool `json:"authenticatedEndpoints,omitempty"` diff --git a/exchanges/sharedtestvalues/sharedtestvalues.go b/exchanges/sharedtestvalues/sharedtestvalues.go index 254e9156..3bd871a9 100644 --- a/exchanges/sharedtestvalues/sharedtestvalues.go +++ b/exchanges/sharedtestvalues/sharedtestvalues.go @@ -1,6 +1,10 @@ package sharedtestvalues -import "time" +import ( + "time" + + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" +) // This package is only to be referenced in test files const ( @@ -29,3 +33,17 @@ func GetWebsocketInterfaceChannelOverride() chan interface{} { func GetWebsocketStructChannelOverride() chan struct{} { return make(chan struct{}, WebsocketChannelOverrideCapacity) } + +// NewTestWebsocket returns a test websocket object +func NewTestWebsocket() *stream.Websocket { + return &stream.Websocket{ + Init: true, + DataHandler: make(chan interface{}, 75), + ToRoutine: make(chan interface{}, 1000), + TrafficAlert: make(chan struct{}), + ReadMessageErrors: make(chan error), + Subscribe: make(chan []stream.ChannelSubscription, 10), + Unsubscribe: make(chan []stream.ChannelSubscription, 10), + Match: stream.NewMatch(), + } +} diff --git a/exchanges/stats/stats.go b/exchanges/stats/stats.go index f9805eed..85b145dc 100644 --- a/exchanges/stats/stats.go +++ b/exchanges/stats/stats.go @@ -1,6 +1,7 @@ package stats import ( + "errors" "sort" "github.com/thrasher-corp/gocryptotrader/currency" @@ -50,40 +51,48 @@ func (b ByVolume) Swap(i, j int) { } // Add adds or updates the item stats -func Add(exchange string, p currency.Pair, assetType asset.Item, price, volume float64) { +func Add(exchange string, p currency.Pair, a asset.Item, price, volume float64) error { if exchange == "" || - assetType == "" || + a == "" || price == 0 || volume == 0 || p.Base.IsEmpty() || p.Quote.IsEmpty() { - return + return errors.New("cannot add or update, invalid params") } if p.Base == currency.XBT { - newPair := currency.NewPairFromStrings(currency.BTC.String(), p.Quote.String()) - Append(exchange, newPair, assetType, price, volume) + newPair, err := currency.NewPairFromStrings(currency.BTC.String(), + p.Quote.String()) + if err != nil { + return err + } + Append(exchange, newPair, a, price, volume) } if p.Quote == currency.USDT { - newPair := currency.NewPairFromStrings(p.Base.String(), currency.USD.String()) - Append(exchange, newPair, assetType, price, volume) + newPair, err := currency.NewPairFromStrings(p.Base.String(), currency.USD.String()) + if err != nil { + return err + } + Append(exchange, newPair, a, price, volume) } - Append(exchange, p, assetType, price, volume) + Append(exchange, p, a, price, volume) + return nil } // Append adds or updates the item stats for a specific // currency pair and asset type -func Append(exchange string, p currency.Pair, assetType asset.Item, price, volume float64) { - if AlreadyExists(exchange, p, assetType, price, volume) { +func Append(exchange string, p currency.Pair, a asset.Item, price, volume float64) { + if AlreadyExists(exchange, p, a, price, volume) { return } i := Item{ Exchange: exchange, Pair: p, - AssetType: assetType, + AssetType: a, Price: price, Volume: volume, } diff --git a/exchanges/stats/stats_test.go b/exchanges/stats/stats_test.go index 06aea628..ad8c80a2 100644 --- a/exchanges/stats/stats_test.go +++ b/exchanges/stats/stats_test.go @@ -12,7 +12,10 @@ const ( ) func TestLenByPrice(t *testing.T) { - p := currency.NewPairFromStrings("BTC", "USD") + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } Items = []Item{ { Exchange: testExchange, @@ -29,8 +32,10 @@ func TestLenByPrice(t *testing.T) { } func TestLessByPrice(t *testing.T) { - p := currency.NewPairFromStrings("BTC", "USD") - + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } Items = []Item{ { Exchange: "alphapoint", @@ -57,8 +62,10 @@ func TestLessByPrice(t *testing.T) { } func TestSwapByPrice(t *testing.T) { - p := currency.NewPairFromStrings("BTC", "USD") - + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } Items = []Item{ { Exchange: "bitstamp", @@ -107,27 +114,42 @@ func TestSwapByVolume(t *testing.T) { func TestAdd(t *testing.T) { Items = Items[:0] - p := currency.NewPairFromStrings("BTC", "USD") - Add(testExchange, p, asset.Spot, 1200, 42) + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + err = Add(testExchange, p, asset.Spot, 1200, 42) + if err != nil { + t.Fatal(err) + } if len(Items) < 1 { t.Error("stats Add did not add exchange info.") } - Add("", p, "", 0, 0) + err = Add("", p, "", 0, 0) + if err == nil { + t.Fatal("error cannot be nil") + } if len(Items) != 1 { t.Error("stats Add did not add exchange info.") } p.Base = currency.XBT - Add(testExchange, p, asset.Spot, 1201, 43) + err = Add(testExchange, p, asset.Spot, 1201, 43) + if err != nil { + t.Fatal(err) + } if Items[1].Pair.String() != "XBTUSD" { t.Fatal("stats Add did not add exchange info.") } - p = currency.NewPairFromStrings("ETH", "USDT") + p, err = currency.NewPairFromStrings("ETH", "USDT") + if err != nil { + t.Fatal(err) + } Add(testExchange, p, asset.Spot, 300, 1000) if Items[2].Pair.String() != "ETHUSD" { @@ -136,7 +158,10 @@ func TestAdd(t *testing.T) { } func TestAppend(t *testing.T) { - p := currency.NewPairFromStrings("BTC", "USD") + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } Append("sillyexchange", p, asset.Spot, 1234, 45) if len(Items) < 2 { t.Error("stats Append did not add exchange values.") @@ -149,7 +174,10 @@ func TestAppend(t *testing.T) { } func TestAlreadyExists(t *testing.T) { - p := currency.NewPairFromStrings("BTC", "USD") + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } if !AlreadyExists(testExchange, p, asset.Spot, 1200, 42) { t.Error("stats AlreadyExists exchange does not exist.") } @@ -160,7 +188,10 @@ func TestAlreadyExists(t *testing.T) { } func TestSortExchangesByVolume(t *testing.T) { - p := currency.NewPairFromStrings("BTC", "USD") + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } topVolume := SortExchangesByVolume(p, asset.Spot, true) if topVolume[0].Exchange != "sillyexchange" { t.Error("stats SortExchangesByVolume incorrectly sorted values.") @@ -173,7 +204,10 @@ func TestSortExchangesByVolume(t *testing.T) { } func TestSortExchangesByPrice(t *testing.T) { - p := currency.NewPairFromStrings("BTC", "USD") + p, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } topPrice := SortExchangesByPrice(p, asset.Spot, true) if topPrice[0].Exchange != "sillyexchange" { t.Error("stats SortExchangesByPrice incorrectly sorted values.") diff --git a/exchanges/websocket/wsorderbook/wsorderbook.go b/exchanges/stream/buffer/buffer.go similarity index 81% rename from exchanges/websocket/wsorderbook/wsorderbook.go rename to exchanges/stream/buffer/buffer.go index 169eb0a2..7ef4d90e 100644 --- a/exchanges/websocket/wsorderbook/wsorderbook.go +++ b/exchanges/stream/buffer/buffer.go @@ -1,4 +1,4 @@ -package wsorderbook +package buffer import ( "errors" @@ -11,21 +11,22 @@ import ( ) // Setup sets private variables -func (w *WebsocketOrderbookLocal) Setup(obBufferLimit int, bufferEnabled, sortBuffer, sortBufferByUpdateIDs, updateEntriesByID bool, exchangeName string) { +func (w *Orderbook) Setup(obBufferLimit int, bufferEnabled, sortBuffer, sortBufferByUpdateIDs, updateEntriesByID bool, exchangeName string, dataHandler chan interface{}) { w.obBufferLimit = obBufferLimit w.bufferEnabled = bufferEnabled w.sortBuffer = sortBuffer w.sortBufferByUpdateIDs = sortBufferByUpdateIDs w.updateEntriesByID = updateEntriesByID w.exchangeName = exchangeName + w.dataHandler = dataHandler } -// Update updates a local cache using bid targets and ask targets then updates +// Update updates a local buffer using bid targets and ask targets then updates // main orderbook // Volume == 0; deletion at price target // Price target not found; append of price target // Price target found; amend volume of price target -func (w *WebsocketOrderbookLocal) Update(u *WebsocketOrderbookUpdate) error { +func (w *Orderbook) Update(u *Update) error { if (u.Bids == nil && u.Asks == nil) || (len(u.Bids) == 0 && len(u.Asks) == 0) { return fmt.Errorf("%v cannot have bids and ask targets both nil", w.exchangeName) @@ -52,19 +53,23 @@ func (w *WebsocketOrderbookLocal) Update(u *WebsocketOrderbookUpdate) error { if err != nil { return err } + if w.bufferEnabled { // Reset the buffer w.buffer[u.Pair][u.Asset] = nil } + + // Process in data handler + w.dataHandler <- obLookup return nil } -func (w *WebsocketOrderbookLocal) processBufferUpdate(o *orderbook.Base, u *WebsocketOrderbookUpdate) bool { +func (w *Orderbook) processBufferUpdate(o *orderbook.Base, u *Update) bool { if w.buffer == nil { - w.buffer = make(map[currency.Pair]map[asset.Item][]*WebsocketOrderbookUpdate) + w.buffer = make(map[currency.Pair]map[asset.Item][]*Update) } if w.buffer[u.Pair] == nil { - w.buffer[u.Pair] = make(map[asset.Item][]*WebsocketOrderbookUpdate) + w.buffer[u.Pair] = make(map[asset.Item][]*Update) } bufferLookup := w.buffer[u.Pair][u.Asset] if len(bufferLookup) <= w.obBufferLimit { @@ -93,7 +98,7 @@ func (w *WebsocketOrderbookLocal) processBufferUpdate(o *orderbook.Base, u *Webs return true } -func (w *WebsocketOrderbookLocal) processObUpdate(o *orderbook.Base, u *WebsocketOrderbookUpdate) { +func (w *Orderbook) processObUpdate(o *orderbook.Base, u *Update) { o.LastUpdateID = u.UpdateID if w.updateEntriesByID { @@ -104,7 +109,7 @@ func (w *WebsocketOrderbookLocal) processObUpdate(o *orderbook.Base, u *Websocke } } -func (w *WebsocketOrderbookLocal) updateAsksByPrice(o *orderbook.Base, u *WebsocketOrderbookUpdate) { +func (w *Orderbook) updateAsksByPrice(o *orderbook.Base, u *Update) { updates: for j := range u.Asks { for k := range o.Asks { @@ -127,7 +132,7 @@ updates: }) } -func (w *WebsocketOrderbookLocal) updateBidsByPrice(o *orderbook.Base, u *WebsocketOrderbookUpdate) { +func (w *Orderbook) updateBidsByPrice(o *orderbook.Base, u *Update) { updates: for j := range u.Bids { for k := range o.Bids { @@ -152,7 +157,7 @@ updates: // updateByIDAndAction will receive an action to execute against the orderbook // it will then match by IDs instead of price to perform the action -func (w *WebsocketOrderbookLocal) updateByIDAndAction(o *orderbook.Base, u *WebsocketOrderbookUpdate) { +func (w *Orderbook) updateByIDAndAction(o *orderbook.Base, u *Update) { switch u.Action { case "update": for x := range u.Bids { @@ -227,7 +232,7 @@ func (w *WebsocketOrderbookLocal) updateByIDAndAction(o *orderbook.Base, u *Webs // LoadSnapshot loads initial snapshot of ob data, overwrite allows full // ob to be completely rewritten because the exchange is a doing a full // update not an incremental one -func (w *WebsocketOrderbookLocal) LoadSnapshot(newOrderbook *orderbook.Base) error { +func (w *Orderbook) LoadSnapshot(newOrderbook *orderbook.Base) error { if len(newOrderbook.Asks) == 0 || len(newOrderbook.Bids) == 0 { return fmt.Errorf("%v snapshot ask and bids are nil", w.exchangeName) } @@ -254,21 +259,27 @@ func (w *WebsocketOrderbookLocal) LoadSnapshot(newOrderbook *orderbook.Base) err } w.ob[newOrderbook.Pair][newOrderbook.AssetType] = newOrderbook - return newOrderbook.Process() + err := newOrderbook.Process() + if err != nil { + return err + } + + w.dataHandler <- newOrderbook + return nil } // GetOrderbook use sparingly. Modifying anything here will ruin hash // calculation and cause problems -func (w *WebsocketOrderbookLocal) GetOrderbook(p currency.Pair, a asset.Item) *orderbook.Base { +func (w *Orderbook) GetOrderbook(p currency.Pair, a asset.Item) *orderbook.Base { w.m.Lock() ob := w.ob[p][a] w.m.Unlock() return ob } -// FlushCache flushes w.ob data to be garbage collected and refreshed when a +// FlushBuffer flushes w.ob data to be garbage collected and refreshed when a // connection is lost and reconnected -func (w *WebsocketOrderbookLocal) FlushCache() { +func (w *Orderbook) FlushBuffer() { w.m.Lock() w.ob = nil w.buffer = nil diff --git a/exchanges/websocket/wsorderbook/wsorderbook_test.go b/exchanges/stream/buffer/buffer_test.go similarity index 92% rename from exchanges/websocket/wsorderbook/wsorderbook_test.go rename to exchanges/stream/buffer/buffer_test.go index 0393712e..a62649b2 100644 --- a/exchanges/websocket/wsorderbook/wsorderbook_test.go +++ b/exchanges/stream/buffer/buffer_test.go @@ -1,4 +1,4 @@ -package wsorderbook +package buffer import ( "fmt" @@ -20,13 +20,13 @@ var itemArray = [][]orderbook.Item{ {{Price: 5000, Amount: 1, ID: 5}}, } -var cp = currency.NewPairFromString("BTCUSD") +var cp, _ = currency.NewPairFromString("BTCUSD") const ( exchangeName = "exchangeTest" ) -func createSnapshot() (obl *WebsocketOrderbookLocal, asks, bids []orderbook.Item, err error) { +func createSnapshot() (obl *Orderbook, asks, bids []orderbook.Item, err error) { var snapShot1 orderbook.Base snapShot1.ExchangeName = exchangeName asks = []orderbook.Item{ @@ -39,7 +39,7 @@ func createSnapshot() (obl *WebsocketOrderbookLocal, asks, bids []orderbook.Item snapShot1.Bids = bids snapShot1.AssetType = asset.Spot snapShot1.Pair = cp - obl = &WebsocketOrderbookLocal{exchangeName: exchangeName} + obl = &Orderbook{exchangeName: exchangeName, dataHandler: make(chan interface{}, 100)} err = obl.LoadSnapshot(&snapShot1) return } @@ -69,7 +69,7 @@ func BenchmarkUpdateBidsByPrice(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { bidAsks := bidAskGenerator() - update := &WebsocketOrderbookUpdate{ + update := &Update{ Bids: bidAsks, Asks: bidAsks, Pair: cp, @@ -88,7 +88,7 @@ func BenchmarkUpdateAsksByPrice(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { bidAsks := bidAskGenerator() - update := &WebsocketOrderbookUpdate{ + update := &Update{ Bids: bidAsks, Asks: bidAsks, Pair: cp, @@ -115,7 +115,7 @@ func BenchmarkBufferPerformance(b *testing.B) { ID: 1337, } obl.ob[cp][asset.Spot].Bids = append(obl.ob[cp][asset.Spot].Bids, dummyItem) - update := &WebsocketOrderbookUpdate{ + update := &Update{ Bids: bids, Asks: asks, Pair: cp, @@ -150,7 +150,7 @@ func BenchmarkBufferSortingPerformance(b *testing.B) { ID: 1337, } obl.ob[cp][asset.Spot].Bids = append(obl.ob[cp][asset.Spot].Bids, dummyItem) - update := &WebsocketOrderbookUpdate{ + update := &Update{ Bids: bids, Asks: asks, Pair: cp, @@ -186,7 +186,7 @@ func BenchmarkBufferSortingByIDPerformance(b *testing.B) { ID: 1337, } obl.ob[cp][asset.Spot].Bids = append(obl.ob[cp][asset.Spot].Bids, dummyItem) - update := &WebsocketOrderbookUpdate{ + update := &Update{ Bids: bids, Asks: asks, Pair: cp, @@ -220,7 +220,7 @@ func BenchmarkNoBufferPerformance(b *testing.B) { ID: 1337, } obl.ob[cp][asset.Spot].Bids = append(obl.ob[cp][asset.Spot].Bids, dummyItem) - update := &WebsocketOrderbookUpdate{ + update := &Update{ Bids: bids, Asks: asks, Pair: cp, @@ -245,7 +245,7 @@ func TestUpdates(t *testing.T) { t.Error(err) } - obl.updateAsksByPrice(obl.ob[cp][asset.Spot], &WebsocketOrderbookUpdate{ + obl.updateAsksByPrice(obl.ob[cp][asset.Spot], &Update{ Bids: itemArray[5], Asks: itemArray[5], Pair: cp, @@ -256,7 +256,7 @@ func TestUpdates(t *testing.T) { t.Error(err) } - obl.updateAsksByPrice(obl.ob[cp][asset.Spot], &WebsocketOrderbookUpdate{ + obl.updateAsksByPrice(obl.ob[cp][asset.Spot], &Update{ Bids: itemArray[0], Asks: itemArray[0], Pair: cp, @@ -283,7 +283,7 @@ func TestHittingTheBuffer(t *testing.T) { for i := range itemArray { asks := itemArray[i] bids := itemArray[i] - err = obl.Update(&WebsocketOrderbookUpdate{ + err = obl.Update(&Update{ Bids: bids, Asks: asks, Pair: cp, @@ -317,7 +317,7 @@ func TestInsertWithIDs(t *testing.T) { for i := range itemArray { asks := itemArray[i] bids := itemArray[i] - err = obl.Update(&WebsocketOrderbookUpdate{ + err = obl.Update(&Update{ Bids: bids, Asks: asks, Pair: cp, @@ -352,7 +352,7 @@ func TestSortIDs(t *testing.T) { for i := range itemArray { asks := itemArray[i] bids := itemArray[i] - err = obl.Update(&WebsocketOrderbookUpdate{ + err = obl.Update(&Update{ Bids: bids, Asks: asks, Pair: cp, @@ -397,7 +397,7 @@ func TestDeleteWithIDs(t *testing.T) { for i := range itemArray { asks := itemArray[i] bids := itemArray[i] - err = obl.Update(&WebsocketOrderbookUpdate{ + err = obl.Update(&Update{ Bids: bids, Asks: asks, Pair: cp, @@ -430,7 +430,7 @@ func TestUpdateWithIDs(t *testing.T) { for i := range itemArray { asks := itemArray[i] bids := itemArray[i] - err = obl.Update(&WebsocketOrderbookUpdate{ + err = obl.Update(&Update{ Bids: bids, Asks: asks, Pair: cp, @@ -469,7 +469,7 @@ func TestOutOfOrderIDs(t *testing.T) { obl.obBufferLimit = 5 for i := range itemArray { asks := itemArray[i] - err = obl.Update(&WebsocketOrderbookUpdate{ + err = obl.Update(&Update{ Asks: asks, Pair: cp, UpdateID: outOFOrderIDs[i], @@ -498,7 +498,7 @@ func TestOrderbookLastUpdateID(t *testing.T) { for i := range itemArray { asks := itemArray[i] - err = obl.Update(&WebsocketOrderbookUpdate{ + err = obl.Update(&Update{ Asks: asks, Pair: cp, UpdateID: int64(i) + 1, @@ -517,7 +517,7 @@ func TestOrderbookLastUpdateID(t *testing.T) { // TestRunUpdateWithoutSnapshot logic test func TestRunUpdateWithoutSnapshot(t *testing.T) { - var obl WebsocketOrderbookLocal + var obl Orderbook var snapShot1 orderbook.Base asks := []orderbook.Item{ {Price: 4000, Amount: 1, ID: 8}, @@ -531,7 +531,7 @@ func TestRunUpdateWithoutSnapshot(t *testing.T) { snapShot1.AssetType = asset.Spot snapShot1.Pair = cp obl.exchangeName = exchangeName - err := obl.Update(&WebsocketOrderbookUpdate{ + err := obl.Update(&Update{ Bids: bids, Asks: asks, Pair: cp, @@ -548,14 +548,14 @@ func TestRunUpdateWithoutSnapshot(t *testing.T) { // TestRunUpdateWithoutAnyUpdates logic test func TestRunUpdateWithoutAnyUpdates(t *testing.T) { - var obl WebsocketOrderbookLocal + var obl Orderbook var snapShot1 orderbook.Base snapShot1.Asks = []orderbook.Item{} snapShot1.Bids = []orderbook.Item{} snapShot1.AssetType = asset.Spot snapShot1.Pair = cp obl.exchangeName = exchangeName - err := obl.Update(&WebsocketOrderbookUpdate{ + err := obl.Update(&Update{ Bids: snapShot1.Asks, Asks: snapShot1.Bids, Pair: cp, @@ -573,7 +573,7 @@ func TestRunUpdateWithoutAnyUpdates(t *testing.T) { // TestRunSnapshotWithNoData logic test func TestRunSnapshotWithNoData(t *testing.T) { - var obl WebsocketOrderbookLocal + var obl Orderbook var snapShot1 orderbook.Base snapShot1.Asks = []orderbook.Item{} snapShot1.Bids = []orderbook.Item{} @@ -592,7 +592,8 @@ func TestRunSnapshotWithNoData(t *testing.T) { // TestLoadSnapshot logic test func TestLoadSnapshot(t *testing.T) { - var obl WebsocketOrderbookLocal + var obl Orderbook + obl.dataHandler = make(chan interface{}, 100) var snapShot1 orderbook.Base snapShot1.ExchangeName = "SnapshotWithOverride" asks := []orderbook.Item{ @@ -611,8 +612,8 @@ func TestLoadSnapshot(t *testing.T) { } } -// TestFlushCache logic test -func TestFlushCache(t *testing.T) { +// TestFlushbuffer logic test +func TestFlushbuffer(t *testing.T) { obl, _, _, err := createSnapshot() if err != nil { t.Fatal(err) @@ -620,7 +621,7 @@ func TestFlushCache(t *testing.T) { if obl.ob[cp][asset.Spot] == nil { t.Error("expected ob to have ask entries") } - obl.FlushCache() + obl.FlushBuffer() if obl.ob[cp][asset.Spot] != nil { t.Error("expected ob be flushed") } @@ -628,7 +629,8 @@ func TestFlushCache(t *testing.T) { // TestInsertingSnapShots logic test func TestInsertingSnapShots(t *testing.T) { - var obl WebsocketOrderbookLocal + var obl Orderbook + obl.dataHandler = make(chan interface{}, 100) var snapShot1 orderbook.Base snapShot1.ExchangeName = "WSORDERBOOKTEST1" asks := []orderbook.Item{ @@ -700,7 +702,10 @@ func TestInsertingSnapShots(t *testing.T) { snapShot2.Asks = asks snapShot2.Bids = bids snapShot2.AssetType = asset.Spot - snapShot2.Pair = currency.NewPairFromString("LTCUSD") + snapShot2.Pair, err = currency.NewPairFromString("LTCUSD") + if err != nil { + t.Fatal(err) + } err = obl.LoadSnapshot(&snapShot2) if err != nil { t.Fatal(err) @@ -738,7 +743,10 @@ func TestInsertingSnapShots(t *testing.T) { snapShot3.Asks = asks snapShot3.Bids = bids snapShot3.AssetType = "FUTURES" - snapShot3.Pair = currency.NewPairFromString("LTCUSD") + snapShot3.Pair, err = currency.NewPairFromString("LTCUSD") + if err != nil { + t.Fatal(err) + } err = obl.LoadSnapshot(&snapShot3) if err != nil { t.Fatal(err) @@ -772,8 +780,8 @@ func TestGetOrderbook(t *testing.T) { } func TestSetup(t *testing.T) { - w := WebsocketOrderbookLocal{} - w.Setup(1, true, true, true, true, "hi") + w := Orderbook{} + w.Setup(1, true, true, true, true, "hi", make(chan interface{})) if w.obBufferLimit != 1 || !w.bufferEnabled || !w.sortBuffer || @@ -791,7 +799,7 @@ func TestEnsureMultipleUpdatesViaPrice(t *testing.T) { } asks := bidAskGenerator() - obl.updateAsksByPrice(obl.ob[cp][asset.Spot], &WebsocketOrderbookUpdate{ + obl.updateAsksByPrice(obl.ob[cp][asset.Spot], &Update{ Bids: asks, Asks: asks, Pair: cp, diff --git a/exchanges/websocket/wsorderbook/wsorderbook_types.go b/exchanges/stream/buffer/buffer_types.go similarity index 65% rename from exchanges/websocket/wsorderbook/wsorderbook_types.go rename to exchanges/stream/buffer/buffer_types.go index 2ae77a29..93020b85 100644 --- a/exchanges/websocket/wsorderbook/wsorderbook_types.go +++ b/exchanges/stream/buffer/buffer_types.go @@ -1,4 +1,4 @@ -package wsorderbook +package buffer import ( "sync" @@ -9,22 +9,23 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" ) -// WebsocketOrderbookLocal defines a local cache of orderbooks for amending, -// appending and deleting changes and updates the main store in wsorderbook.go -type WebsocketOrderbookLocal struct { +// Orderbook defines a local cache of orderbooks for amending, appending +// and deleting changes and updates the main store for a stream +type Orderbook struct { ob map[currency.Pair]map[asset.Item]*orderbook.Base - buffer map[currency.Pair]map[asset.Item][]*WebsocketOrderbookUpdate + buffer map[currency.Pair]map[asset.Item][]*Update obBufferLimit int bufferEnabled bool sortBuffer bool sortBufferByUpdateIDs bool // When timestamps aren't provided, an id can help sort updateEntriesByID bool // Use the update IDs to match ob entries exchangeName string + dataHandler chan interface{} m sync.Mutex } -// WebsocketOrderbookUpdate stores orderbook updates and dictates what features to use when processing -type WebsocketOrderbookUpdate struct { +// Update stores orderbook updates and dictates what features to use when processing +type Update struct { UpdateID int64 // Used when no time is provided UpdateTime time.Time Asset asset.Item diff --git a/exchanges/stream/stream_match.go b/exchanges/stream/stream_match.go new file mode 100644 index 00000000..ecf30583 --- /dev/null +++ b/exchanges/stream/stream_match.go @@ -0,0 +1,81 @@ +package stream + +import ( + "errors" + "sync" +) + +// NewMatch returns a new matcher +func NewMatch() *Match { + return &Match{ + m: make(map[interface{}]chan []byte), + } +} + +// Match is a distributed subtype that handles the matching of requests and +// responses in a timely manner, reducing the need to differentiate between +// connections. Stream systems fan in all incoming payloads to one routine for +// processing. +type Match struct { + m map[interface{}]chan []byte + sync.Mutex +} + +// Incoming matches with request, disregarding the returned payload +func (m *Match) Incoming(signature interface{}) bool { + return m.IncomingWithData(signature, nil) +} + +// IncomingWithData matches with requests and takes in the returned payload, to +// be processed outside of a stream processing routine +func (m *Match) IncomingWithData(signature interface{}, data []byte) bool { + m.Lock() + defer m.Unlock() + ch, ok := m.m[signature] + if ok { + select { + case ch <- data: + default: + // this shouldn't occur but if it does continue to process as normal + return false + } + return true + } + return false +} + +// Sets the signature response channel for incoming data +func (m *Match) set(signature interface{}) (matcher, error) { + var ch chan []byte + m.Lock() + _, ok := m.m[signature] + if ok { + m.Unlock() + return matcher{}, errors.New("signature collision") + } + // This is buffered so we don't need to wait for receiver. + ch = make(chan []byte, 1) + m.m[signature] = ch + m.Unlock() + + return matcher{ + C: ch, + sig: signature, + m: m, + }, nil +} + +// matcher defines a payload matching return mechanism +type matcher struct { + C chan []byte + sig interface{} + m *Match +} + +// Cleanup closes underlying channel and deletes signature from map +func (m *matcher) Cleanup() { + m.m.Lock() + close(m.C) + delete(m.m.m, m.sig) + m.m.Unlock() +} diff --git a/exchanges/stream/stream_match_test.go b/exchanges/stream/stream_match_test.go new file mode 100644 index 00000000..743a2d94 --- /dev/null +++ b/exchanges/stream/stream_match_test.go @@ -0,0 +1,50 @@ +package stream + +import ( + "fmt" + "testing" +) + +func TestMatch(t *testing.T) { + bm := &Match{} + if bm.Incoming("wow") { + t.Fatal("Should not have matched") + } + + nm := NewMatch() + // try to match with unset signature + if nm.Incoming("hello") { + t.Fatal("should not be able to match") + } + + m, err := nm.set("hello") + if err != nil { + t.Fatal(err) + } + + _, err = nm.set("hello") + if err == nil { + t.Fatal("error cannot be nil as this collision cannot occur") + } + + if m.sig != "hello" { + t.Fatal(err) + } + + // try and match with initial payload + if !nm.Incoming("hello") { + t.Fatal("should of matched") + } + + // put in secondary payload with conflicting signature + if nm.Incoming("hello") { + fmt.Println("should not have been able to match") + } + + data := <-m.C + if data != nil { + t.Fatal("wow") + } + + m.Cleanup() +} diff --git a/exchanges/stream/stream_types.go b/exchanges/stream/stream_types.go new file mode 100644 index 00000000..311d26a6 --- /dev/null +++ b/exchanges/stream/stream_types.go @@ -0,0 +1,111 @@ +package stream + +import ( + "net/http" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" + "github.com/thrasher-corp/gocryptotrader/exchanges/order" +) + +// Connection defines a streaming services connection +type Connection interface { + Dial(*websocket.Dialer, http.Header) error + ReadMessage() Response + SendJSONMessage(interface{}) error + SetupPingHandler(PingHandler) + GenerateMessageID(highPrecision bool) int64 + SendMessageReturnResponse(signature interface{}, request interface{}) ([]byte, error) + SendRawMessage(messageType int, message []byte) error + SetURL(string) + SetProxy(string) + GetURL() string + Shutdown() error +} + +// Response defines generalised data from the stream connection +type Response struct { + Type int + Raw []byte +} + +// ChannelSubscription container for streaming subscriptions +type ChannelSubscription struct { + Channel string + Currency currency.Pair + Asset asset.Item + Params map[string]interface{} +} + +// ConnectionSetup defines variables for an individual stream connection +type ConnectionSetup struct { + ResponseCheckTimeout time.Duration + ResponseMaxLimit time.Duration + RateLimit int64 + URL string + Authenticated bool +} + +// PingHandler container for ping handler settings +type PingHandler struct { + Websocket bool + UseGorillaHandler bool + MessageType int + Message []byte + Delay time.Duration +} + +// TradeData defines trade data +type TradeData struct { + Timestamp time.Time + CurrencyPair currency.Pair + AssetType asset.Item + Exchange string + EventType order.Type + Price float64 + Amount float64 + Side order.Side +} + +// FundingData defines funding data +type FundingData struct { + Timestamp time.Time + CurrencyPair currency.Pair + AssetType asset.Item + Exchange string + Amount float64 + Rate float64 + Period int64 + Side order.Side +} + +// KlineData defines kline feed +type KlineData struct { + Timestamp time.Time + Pair currency.Pair + AssetType asset.Item + Exchange string + StartTime time.Time + CloseTime time.Time + Interval string + OpenPrice float64 + ClosePrice float64 + HighPrice float64 + LowPrice float64 + Volume float64 +} + +// WebsocketPositionUpdated reflects a change in orders/contracts on an exchange +type WebsocketPositionUpdated struct { + Timestamp time.Time + Pair currency.Pair + AssetType asset.Item + Exchange string +} + +// UnhandledMessageWarning defines a container for unhandled message warnings +type UnhandledMessageWarning struct { + Message string +} diff --git a/exchanges/stream/websocket.go b/exchanges/stream/websocket.go new file mode 100644 index 00000000..6ee1e0c7 --- /dev/null +++ b/exchanges/stream/websocket.go @@ -0,0 +1,935 @@ +package stream + +import ( + "errors" + "fmt" + "net" + "net/url" + "strings" + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/config" + "github.com/thrasher-corp/gocryptotrader/log" +) + +const ( + defaultJobBuffer = 1000 + // defaultTrafficPeriod defines a period of pause for the traffic monitor, + // as there are periods with large incoming traffic alerts which requires a + // timer reset, this limits work on this routine to a more effective rate + // of check. + defaultTrafficPeriod = time.Second +) + +// New initialises the websocket struct +func New() *Websocket { + return &Websocket{ + Init: true, + DataHandler: make(chan interface{}), + ToRoutine: make(chan interface{}, defaultJobBuffer), + TrafficAlert: make(chan struct{}), + ReadMessageErrors: make(chan error), + Subscribe: make(chan []ChannelSubscription), + Unsubscribe: make(chan []ChannelSubscription), + Match: NewMatch(), + } +} + +// Setup sets main variables for websocket connection +func (w *Websocket) Setup(s *WebsocketSetup) error { + if w == nil { + return errors.New("websocket is nil") + } + + if !w.Init { + return fmt.Errorf("%s Websocket already initialised", + s.ExchangeName) + } + + w.verbose = s.Verbose + if s.Features == nil { + return errors.New("websocket features is unset") + } + + w.features = s.Features + + if w.features.Subscribe && s.Subscriber == nil { + return errors.New("features have been set yet channel subscriber is not set") + } + w.Subscriber = s.Subscriber + + if w.features.Unsubscribe && s.UnSubscriber == nil { + return errors.New("features have been set yet channel unsubscriber is not set") + } + w.Unsubscriber = s.UnSubscriber + + w.GenerateSubs = s.GenerateSubscriptions + + w.enabled = s.Enabled + if s.DefaultURL == "" { + return errors.New("default url is empty") + } + w.defaultURL = s.DefaultURL + if s.RunningURL == "" { + return errors.New("running URL cannot be nil") + } + err := w.SetWebsocketURL(s.RunningURL, false, false) + if err != nil { + return err + } + + if s.RunningURLAuth != "" { + err = w.SetWebsocketURL(s.RunningURLAuth, true, false) + if err != nil { + return err + } + } + + w.connector = s.Connector + if s.ExchangeName == "" { + return errors.New("exchange name unset") + } + w.exchangeName = s.ExchangeName + + if s.WebsocketTimeout < time.Second { + return fmt.Errorf("traffic timeout cannot be less than %s", time.Second) + } + + w.trafficTimeout = s.WebsocketTimeout + if s.Features == nil { + return errors.New("feature set is nil") + } + + w.ShutdownC = make(chan struct{}) + w.Wg = new(sync.WaitGroup) + + w.SetCanUseAuthenticatedEndpoints(s.AuthenticatedWebsocketAPISupport) + err = w.Initialise() + if err != nil { + return err + } + + w.Orderbook.Setup(s.OrderbookBufferLimit, + s.BufferEnabled, + s.SortBuffer, + s.SortBufferByUpdateIDs, + s.UpdateEntriesByID, + w.exchangeName, + w.DataHandler) + return nil +} + +// SetupNewConnection sets up an auth or unauth streaming connection +func (w *Websocket) SetupNewConnection(c ConnectionSetup) error { + if w == nil { + return errors.New("setting up new connection error: websocket is nil") + } + if c == (ConnectionSetup{}) { + return errors.New("setting up new connection error: websocket connection configuration empty") + } + + if w.exchangeName == "" { + return errors.New("setting up new connection error: exchange name not set, please call setup first") + } + + if w.TrafficAlert == nil { + return errors.New("setting up new connection error: traffic alert is nil, please call setup first") + } + + if w.ReadMessageErrors == nil { + return errors.New("setting up new connection error: read message errors is nil, please call setup first") + } + + connectionURL := w.GetWebsocketURL() + if c.URL != "" { + connectionURL = c.URL + } + + newConn := &WebsocketConnection{ + ExchangeName: w.exchangeName, + URL: connectionURL, + ProxyURL: w.GetProxyAddress(), + Verbose: w.verbose, + ResponseMaxLimit: c.ResponseMaxLimit, + Traffic: w.TrafficAlert, + readMessageErrors: w.ReadMessageErrors, + ShutdownC: w.ShutdownC, + Wg: w.Wg, + Match: w.Match, + RateLimit: c.RateLimit, + } + + if c.Authenticated { + w.AuthConn = newConn + } else { + w.Conn = newConn + } + + return nil +} + +// Connect initiates a websocket connection by using a package defined connection +// function +func (w *Websocket) Connect() error { + if w.connector == nil { + return errors.New("websocket connect function not set, cannot continue") + } + w.m.Lock() + defer w.m.Unlock() + + if !w.IsEnabled() { + return errors.New(WebsocketNotEnabled) + } + if w.IsConnecting() { + return fmt.Errorf("%v Websocket already attempting to connect", + w.exchangeName) + } + if w.IsConnected() { + return fmt.Errorf("%v Websocket already connected", + w.exchangeName) + } + w.setConnectingStatus(true) + + w.dataMonitor() + + err := w.trafficMonitor() + if err != nil { + return err + } + + // flush any subscriptions from last connection if needed + w.subscriptionMutex.Lock() + w.subscriptions = nil + w.subscriptionMutex.Unlock() + + err = w.connector() + if err != nil { + w.setConnectingStatus(false) + return fmt.Errorf("%v Error connecting %s", + w.exchangeName, err) + } + + w.setConnectedStatus(true) + w.setConnectingStatus(false) + w.setInit(true) + + if !w.IsConnectionMonitorRunning() { + go w.connectionMonitor() + } + + return nil +} + +// Disable disables the exchange websocket protocol +func (w *Websocket) Disable() error { + if !w.IsConnected() || !w.IsEnabled() { + return fmt.Errorf("websocket is already disabled for exchange %s", + w.exchangeName) + } + + w.setEnabled(false) + return nil +} + +// Enable enables the exchange websocket protocol +func (w *Websocket) Enable() error { + if w.IsConnected() || w.IsEnabled() { + return fmt.Errorf("websocket is already enabled for exchange %s", + w.exchangeName) + } + + w.setEnabled(true) + return w.Connect() +} + +// dataMonitor monitors job throughput and logs if there is a back log of data +func (w *Websocket) dataMonitor() { + if w.IsDataMonitorRunning() { + return + } + w.setDataMonitorRunning(true) + w.Wg.Add(1) + + go func() { + defer func() { + for { + // Bleeds data from the websocket connection if needed + select { + case <-w.DataHandler: + default: + w.setDataMonitorRunning(false) + w.Wg.Done() + return + } + } + }() + + for { + select { + case <-w.ShutdownC: + return + case d := <-w.DataHandler: + select { + case w.ToRoutine <- d: + case <-w.ShutdownC: + return + default: + log.Warnf(log.WebsocketMgr, + "%s exchange backlog in websocket processing detected", + w.exchangeName) + select { + case w.ToRoutine <- d: + case <-w.ShutdownC: + return + } + } + } + } + }() +} + +// connectionMonitor ensures that the WS keeps connecting +func (w *Websocket) connectionMonitor() { + if w.IsConnectionMonitorRunning() { + return + } + w.setConnectionMonitorRunning(true) + timer := time.NewTimer(connectionMonitorDelay) + + for { + if w.verbose { + log.Debugf(log.WebsocketMgr, + "%v websocket: running connection monitor cycle\n", + w.exchangeName) + } + if !w.IsEnabled() { + if w.verbose { + log.Debugf(log.WebsocketMgr, + "%v websocket: connectionMonitor - websocket disabled, shutting down\n", + w.exchangeName) + } + if w.IsConnected() { + err := w.Shutdown() + if err != nil { + log.Error(log.WebsocketMgr, err) + } + } + if w.verbose { + log.Debugf(log.WebsocketMgr, + "%v websocket: connection monitor exiting\n", + w.exchangeName) + } + timer.Stop() + w.setConnectionMonitorRunning(false) + return + } + select { + case err := <-w.ReadMessageErrors: + // check if this error is a disconnection error + if isDisconnectionError(err) { + w.setInit(false) + log.Warnf(log.WebsocketMgr, + "%v websocket has been disconnected. Reason: %v", + w.exchangeName, err) + w.setConnectedStatus(false) + } else { + // pass off non disconnect errors to datahandler to manage + w.DataHandler <- err + } + case <-timer.C: + if !w.IsConnecting() && !w.IsConnected() { + err := w.Connect() + if err != nil { + log.Error(log.WebsocketMgr, err) + } + } + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + timer.Reset(connectionMonitorDelay) + } + } +} + +// Shutdown attempts to shut down a websocket connection and associated routines +// by using a package defined shutdown function +func (w *Websocket) Shutdown() error { + w.m.Lock() + defer w.m.Unlock() + + if !w.IsConnected() { + return fmt.Errorf("%v websocket: cannot shutdown a disconnected websocket", + w.exchangeName) + } + + if w.IsConnecting() { + return fmt.Errorf("%v websocket: cannot shutdown, in the process of reconnection", + w.exchangeName) + } + + if w.verbose { + log.Debugf(log.WebsocketMgr, + "%v websocket: shutting down websocket\n", + w.exchangeName) + } + + defer w.Orderbook.FlushBuffer() + + if w.Conn != nil { + if err := w.Conn.Shutdown(); err != nil { + return err + } + } + + if w.AuthConn != nil { + if err := w.AuthConn.Shutdown(); err != nil { + return err + } + } + + // flush any subscriptions from last connection if needed + w.subscriptionMutex.Lock() + w.subscriptions = nil + w.subscriptionMutex.Unlock() + + close(w.ShutdownC) + w.Wg.Wait() + w.ShutdownC = make(chan struct{}) + w.setConnectedStatus(false) + w.setConnectingStatus(false) + if w.verbose { + log.Debugf(log.WebsocketMgr, + "%v websocket: completed websocket shutdown\n", + w.exchangeName) + } + return nil +} + +// FlushChannels flushes channel subscriptions when there is a pair/asset change +func (w *Websocket) FlushChannels() error { + if !w.IsEnabled() { + return fmt.Errorf("%s websocket: service not enabled", w.exchangeName) + } + + if !w.IsConnected() { + return fmt.Errorf("%s websocket: service not connected", w.exchangeName) + } + + if w.features.Subscribe { + newsubs, err := w.GenerateSubs() + if err != nil { + return err + } + + subs, unsubs := w.GetChannelDifference(newsubs) + if w.features.Unsubscribe { + if len(unsubs) != 0 { + err := w.UnsubscribeChannels(unsubs) + if err != nil { + return err + } + } + + if len(subs) != 0 { + return w.SubscribeToChannels(subs) + } + + return nil + } else if len(unsubs) == 0 { + if len(subs) == 0 { + return nil + } + return w.SubscribeToChannels(subs) + } + // FullPayloadSubscribe means that the endpoint requires all + // subscriptions to be sent via the websocket connection e.g. if you are + // subscribed to ticker and orderbook but require trades as well, you + // would need to send ticker, orderbook and trades channel subscription + // messages. + } else if w.features.FullPayloadSubscribe { + newsubs, err := w.GenerateSubs() + if err != nil { + return err + } + + if len(newsubs) != 0 { + return w.SubscribeToChannels(newsubs) + } + return nil + } + + err := w.Shutdown() + if err != nil { + return err + } + return w.Connect() +} + +// trafficMonitor uses a timer of WebsocketTrafficLimitTime and once it expires, +// it will reconnect if the TrafficAlert channel has not received any data. The +// trafficTimer will reset on each traffic alert +func (w *Websocket) trafficMonitor() error { + if w.IsTrafficMonitorRunning() { + return errors.New("traffic monitor already running") + } + w.setTrafficMonitorRunning(true) + w.Wg.Add(1) + + go func() { + var trafficTimer = time.NewTimer(w.trafficTimeout) + pause := make(chan struct{}) + for { + select { + case <-w.ShutdownC: + if w.verbose { + log.Debugf(log.WebsocketMgr, + "%v websocket: trafficMonitor shutdown message received\n", + w.exchangeName) + } + trafficTimer.Stop() + w.setTrafficMonitorRunning(false) + w.Wg.Done() + return + case <-w.TrafficAlert: + if !trafficTimer.Stop() { + select { + case <-trafficTimer.C: + default: + } + } + w.setConnectedStatus(true) + trafficTimer.Reset(w.trafficTimeout) + case <-trafficTimer.C: // Falls through when timer runs out + if w.verbose { + log.Warnf(log.WebsocketMgr, + "%v websocket: has not received a traffic alert in %v. Reconnecting", + w.exchangeName, + w.trafficTimeout) + } + trafficTimer.Stop() + w.Wg.Done() + err := w.Shutdown() + if err != nil { + log.Errorf(log.WebsocketMgr, + "%v websocket: trafficMonitor shutdown err: %s", + w.exchangeName, err) + } + w.setTrafficMonitorRunning(false) + return + } + + // Routine pausing mechanism + go func(p chan struct{}) { + time.Sleep(defaultTrafficPeriod) + p <- struct{}{} + }(pause) + select { + case <-w.ShutdownC: + trafficTimer.Stop() + w.setTrafficMonitorRunning(false) + w.Wg.Done() + return + case <-pause: + } + } + }() + return nil +} + +func (w *Websocket) setConnectedStatus(b bool) { + w.connectionMutex.Lock() + w.connected = b + w.connectionMutex.Unlock() +} + +// IsConnected returns status of connection +func (w *Websocket) IsConnected() bool { + w.connectionMutex.RLock() + defer w.connectionMutex.RUnlock() + return w.connected +} + +func (w *Websocket) setConnectingStatus(b bool) { + w.connectionMutex.Lock() + w.connecting = b + w.connectionMutex.Unlock() +} + +// IsConnecting returns status of connecting +func (w *Websocket) IsConnecting() bool { + w.connectionMutex.RLock() + defer w.connectionMutex.RUnlock() + return w.connecting +} + +func (w *Websocket) setEnabled(b bool) { + w.connectionMutex.Lock() + w.enabled = b + w.connectionMutex.Unlock() +} + +// IsEnabled returns status of enabled +func (w *Websocket) IsEnabled() bool { + w.connectionMutex.RLock() + defer w.connectionMutex.RUnlock() + return w.enabled +} + +func (w *Websocket) setInit(b bool) { + w.connectionMutex.Lock() + w.Init = b + w.connectionMutex.Unlock() +} + +// IsInit returns status of init +func (w *Websocket) IsInit() bool { + w.connectionMutex.RLock() + defer w.connectionMutex.RUnlock() + return w.Init +} + +func (w *Websocket) setTrafficMonitorRunning(b bool) { + w.connectionMutex.Lock() + w.trafficMonitorRunning = b + w.connectionMutex.Unlock() +} + +// IsTrafficMonitorRunning returns status of the traffic monitor +func (w *Websocket) IsTrafficMonitorRunning() bool { + w.connectionMutex.RLock() + defer w.connectionMutex.RUnlock() + return w.trafficMonitorRunning +} + +func (w *Websocket) setConnectionMonitorRunning(b bool) { + w.connectionMutex.Lock() + w.connectionMonitorRunning = b + w.connectionMutex.Unlock() +} + +// IsConnectionMonitorRunning returns status of connection monitor +func (w *Websocket) IsConnectionMonitorRunning() bool { + w.connectionMutex.RLock() + defer w.connectionMutex.RUnlock() + return w.connectionMonitorRunning +} + +func (w *Websocket) setDataMonitorRunning(b bool) { + w.connectionMutex.Lock() + w.dataMonitorRunning = b + w.connectionMutex.Unlock() +} + +// IsDataMonitorRunning returns status of data monitor +func (w *Websocket) IsDataMonitorRunning() bool { + w.connectionMutex.RLock() + defer w.connectionMutex.RUnlock() + return w.dataMonitorRunning +} + +// CanUseAuthenticatedWebsocketForWrapper Handles a common check to +// verify whether a wrapper can use an authenticated websocket endpoint +func (w *Websocket) CanUseAuthenticatedWebsocketForWrapper() bool { + if w.IsConnected() && w.CanUseAuthenticatedEndpoints() { + return true + } else if w.IsConnected() && !w.CanUseAuthenticatedEndpoints() { + log.Infof(log.WebsocketMgr, + WebsocketNotAuthenticatedUsingRest, + w.exchangeName) + } + return false +} + +// SetWebsocketURL sets websocket URL and can refresh underlying connections +func (w *Websocket) SetWebsocketURL(url string, auth, reconnect bool) error { + defaultVals := url == "" || url == config.WebsocketURLNonDefaultMessage + if auth { + if defaultVals { + url = w.defaultURLAuth + } + + err := checkWebsocketURL(url) + if err != nil { + return err + } + w.runningURLAuth = url + + if w.verbose { + log.Debugf(log.WebsocketMgr, + "%s websocket: setting authenticated websocket URL: %s\n", + w.exchangeName, + url) + } + + if w.AuthConn != nil { + w.AuthConn.SetURL(url) + } + } else { + if defaultVals { + url = w.defaultURL + } + err := checkWebsocketURL(url) + if err != nil { + return err + } + w.runningURL = url + + if w.verbose { + log.Debugf(log.WebsocketMgr, + "%s websocket: setting unauthenticated websocket URL: %s\n", + w.exchangeName, + url) + } + + if w.Conn != nil { + w.Conn.SetURL(url) + } + } + + if w.IsConnected() && reconnect { + log.Debugf(log.WebsocketMgr, + "%s websocket: flushing websocket connection to %s\n", + w.exchangeName, + url) + return w.Shutdown() + } + return nil +} + +// GetWebsocketURL returns the running websocket URL +func (w *Websocket) GetWebsocketURL() string { + return w.runningURL +} + +// Initialise verifies status and connects +func (w *Websocket) Initialise() error { + if w.IsEnabled() { + if w.IsInit() { + return nil + } + return fmt.Errorf("%v websocket: already initialised", w.exchangeName) + } + w.setEnabled(w.enabled) + return nil +} + +// SetProxyAddress sets websocket proxy address +func (w *Websocket) SetProxyAddress(proxyAddr string) error { + if proxyAddr != "" { + _, err := url.ParseRequestURI(proxyAddr) + if err != nil { + return fmt.Errorf("%v websocket: cannot set proxy address error '%v'", + w.exchangeName, + err) + } + + if w.proxyAddr == proxyAddr { + return fmt.Errorf("%v websocket: cannot set proxy address to the same address '%v'", + w.exchangeName, + w.proxyAddr) + } + + log.Debugf(log.ExchangeSys, + "%s websocket: setting websocket proxy: %s\n", + w.exchangeName, + proxyAddr) + } else { + log.Debugf(log.ExchangeSys, + "%s websocket: removing websocket proxy\n", + w.exchangeName) + } + + if w.Conn != nil { + w.Conn.SetProxy(proxyAddr) + } + if w.AuthConn != nil { + w.AuthConn.SetProxy(proxyAddr) + } + + w.proxyAddr = proxyAddr + if w.IsInit() && w.IsEnabled() { + if w.IsConnected() { + err := w.Shutdown() + if err != nil { + return err + } + } + return w.Connect() + } + return nil +} + +// GetProxyAddress returns the current websocket proxy +func (w *Websocket) GetProxyAddress() string { + return w.proxyAddr +} + +// GetName returns exchange name +func (w *Websocket) GetName() string { + return w.exchangeName +} + +// GetChannelDifference finds the difference between the subscribed channels +// and the new subscription list when pairs are disabled or enabled. +func (w *Websocket) GetChannelDifference(genSubs []ChannelSubscription) (sub, unsub []ChannelSubscription) { + w.subscriptionMutex.Lock() + defer w.subscriptionMutex.Unlock() + +oldsubs: + for x := range w.subscriptions { + for y := range genSubs { + if w.subscriptions[x].Equal(&genSubs[y]) { + continue oldsubs + } + } + unsub = append(unsub, w.subscriptions[x]) + } + +newsubs: + for x := range genSubs { + for y := range w.subscriptions { + if genSubs[x].Equal(&w.subscriptions[y]) { + continue newsubs + } + } + sub = append(sub, genSubs[x]) + } + return +} + +// UnsubscribeChannels unsubscribes from a websocket channel +func (w *Websocket) UnsubscribeChannels(channels []ChannelSubscription) error { + if len(channels) == 0 { + return fmt.Errorf("%s websocket: channels not populated cannot remove", + w.exchangeName) + } + w.subscriptionMutex.Lock() + defer w.subscriptionMutex.Unlock() + +channels: + for x := range channels { + for y := range w.subscriptions { + if channels[x].Equal(&w.subscriptions[y]) { + continue channels + } + } + return fmt.Errorf("%s websocket: subscription not found in list: %+v", + w.exchangeName, + channels[x]) + } + return w.Unsubscriber(channels) +} + +// ResubscribeToChannel resubscribes to channel +func (w *Websocket) ResubscribeToChannel(subscribedChannel *ChannelSubscription) error { + err := w.UnsubscribeChannels([]ChannelSubscription{*subscribedChannel}) + if err != nil { + return err + } + return w.SubscribeToChannels([]ChannelSubscription{*subscribedChannel}) +} + +// SubscribeToChannels appends supplied channels to channelsToSubscribe +func (w *Websocket) SubscribeToChannels(channels []ChannelSubscription) error { + if len(channels) == 0 { + return fmt.Errorf("%s websocket: cannot subscribe no channels supplied", + w.exchangeName) + } + w.subscriptionMutex.Lock() + defer w.subscriptionMutex.Unlock() + for x := range channels { + for y := range w.subscriptions { + if channels[x].Equal(&w.subscriptions[y]) { + return fmt.Errorf("%s websocket: %v already subscribed", + w.exchangeName, + channels[x]) + } + } + } + return w.Subscriber(channels) +} + +// AddSuccessfulSubscriptions adds subscriptions to the subscription lists that +// has been successfully subscribed +func (w *Websocket) AddSuccessfulSubscriptions(channels ...ChannelSubscription) { + w.subscriptions = append(w.subscriptions, channels...) +} + +// RemoveSuccessfulUnsubscriptions removes subscriptions from the subscription +// list that has been successfulling unsubscribed +func (w *Websocket) RemoveSuccessfulUnsubscriptions(channels ...ChannelSubscription) { + for x := range channels { + for y := range w.subscriptions { + if channels[x].Equal(&w.subscriptions[y]) { + w.subscriptions[y] = w.subscriptions[len(w.subscriptions)-1] + w.subscriptions[len(w.subscriptions)-1] = ChannelSubscription{} + w.subscriptions = w.subscriptions[:len(w.subscriptions)-1] + break + } + } + } +} + +// Equal two WebsocketChannelSubscription to determine equality +func (w *ChannelSubscription) Equal(s *ChannelSubscription) bool { + return strings.EqualFold(w.Channel, s.Channel) && + w.Currency.Equal(s.Currency) +} + +// GetSubscriptions returns a copied list of subscriptions +// subscriptions is a private member and cannot be manipulated +func (w *Websocket) GetSubscriptions() []ChannelSubscription { + w.subscriptionMutex.Lock() + defer w.subscriptionMutex.Unlock() + return append(w.subscriptions[:0:0], w.subscriptions...) +} + +// SetCanUseAuthenticatedEndpoints sets canUseAuthenticatedEndpoints val in +// a thread safe manner +func (w *Websocket) SetCanUseAuthenticatedEndpoints(val bool) { + w.subscriptionMutex.Lock() + defer w.subscriptionMutex.Unlock() + w.canUseAuthenticatedEndpoints = val +} + +// CanUseAuthenticatedEndpoints gets canUseAuthenticatedEndpoints val in +// a thread safe manner +func (w *Websocket) CanUseAuthenticatedEndpoints() bool { + w.subscriptionMutex.Lock() + defer w.subscriptionMutex.Unlock() + return w.canUseAuthenticatedEndpoints +} + +// isDisconnectionError Determines if the error sent over chan ReadMessageErrors is a disconnection error +func isDisconnectionError(err error) bool { + if websocket.IsUnexpectedCloseError(err) { + return true + } + switch e := err.(type) { + case *websocket.CloseError: + return true + case *net.OpError: + if e.Err.Error() == "use of closed network connection" { + return false + } + return true + } + return false +} + +// checkWebsocketURL checks for a valid websocket url +func checkWebsocketURL(s string) error { + u, err := url.Parse(s) + if err != nil { + return err + } + if u.Scheme != "ws" && u.Scheme != "wss" { + return fmt.Errorf("cannot set invalid websocket URL %s", s) + } + return nil +} diff --git a/exchanges/stream/websocket_connection.go b/exchanges/stream/websocket_connection.go new file mode 100644 index 00000000..8e1179ee --- /dev/null +++ b/exchanges/stream/websocket_connection.go @@ -0,0 +1,314 @@ +package stream + +import ( + "bytes" + "compress/flate" + "compress/gzip" + "crypto/rand" + "fmt" + "io/ioutil" + "math/big" + "net" + "net/http" + "net/url" + "sync/atomic" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/log" +) + +// SendMessageReturnResponse will send a WS message to the connection and wait +// for response +func (w *WebsocketConnection) SendMessageReturnResponse(signature, request interface{}) ([]byte, error) { + m, err := w.Match.set(signature) + if err != nil { + return nil, err + } + defer m.Cleanup() + + err = w.SendJSONMessage(request) + if err != nil { + return nil, err + } + + timer := time.NewTimer(w.ResponseMaxLimit) + + select { + case payload := <-m.C: + return payload, nil + case <-timer.C: + timer.Stop() + return nil, fmt.Errorf("%s websocket connection: timeout waiting for response with signature: %v", + w.ExchangeName, + signature) + } +} + +// Dial sets proxy urls and then connects to the websocket +func (w *WebsocketConnection) Dial(dialer *websocket.Dialer, headers http.Header) error { + if w.ProxyURL != "" { + proxy, err := url.Parse(w.ProxyURL) + if err != nil { + return err + } + dialer.Proxy = http.ProxyURL(proxy) + } + + var err error + var conStatus *http.Response + + w.Connection, conStatus, err = dialer.Dial(w.URL, headers) + if err != nil { + if conStatus != nil { + return fmt.Errorf("%s websocket connection: %v %v %v Error: %v", + w.ExchangeName, + w.URL, + conStatus, + conStatus.StatusCode, + err) + } + return fmt.Errorf("%s websocket connection: %v Error: %v", + w.ExchangeName, + w.URL, + err) + } + defer conStatus.Body.Close() + + if w.Verbose { + log.Infof(log.WebsocketMgr, + "%v Websocket connected to %s\n", + w.ExchangeName, + w.URL) + } + select { + case w.Traffic <- struct{}{}: + default: + } + w.setConnectedStatus(true) + return nil +} + +// SendJSONMessage sends a JSON encoded message over the connection +func (w *WebsocketConnection) SendJSONMessage(data interface{}) error { + if !w.IsConnected() { + return fmt.Errorf("%s websocket connection: cannot send message to a disconnected websocket", + w.ExchangeName) + } + + w.writeControl.Lock() + defer w.writeControl.Unlock() + + if w.Verbose { + log.Debugf(log.WebsocketMgr, + "%s websocket connection: sending message to websocket %+v\n", + w.ExchangeName, + data) + } + + if w.RateLimit > 0 { + time.Sleep(time.Duration(w.RateLimit) * time.Millisecond) + if !w.IsConnected() { + return fmt.Errorf("%v websocket connection: cannot send message to a disconnected websocket", + w.ExchangeName) + } + } + return w.Connection.WriteJSON(data) +} + +// SendRawMessage sends a message over the connection without JSON encoding it +func (w *WebsocketConnection) SendRawMessage(messageType int, message []byte) error { + if !w.IsConnected() { + return fmt.Errorf("%v websocket connection: cannot send message to a disconnected websocket", + w.ExchangeName) + } + + w.writeControl.Lock() + defer w.writeControl.Unlock() + + if w.Verbose { + log.Debugf(log.WebsocketMgr, + "%v websocket connection: sending message [%s]\n", + w.ExchangeName, + message) + } + if w.RateLimit > 0 { + time.Sleep(time.Duration(w.RateLimit) * time.Millisecond) + if !w.IsConnected() { + return fmt.Errorf("%v websocket connection: cannot send message to a disconnected websocket", + w.ExchangeName) + } + } + if !w.IsConnected() { + return fmt.Errorf("%v websocket connection: cannot send message to a disconnected websocket", + w.ExchangeName) + } + return w.Connection.WriteMessage(messageType, message) +} + +// SetupPingHandler will automatically send ping or pong messages based on +// WebsocketPingHandler configuration +func (w *WebsocketConnection) SetupPingHandler(handler PingHandler) { + if handler.UseGorillaHandler { + h := func(msg string) error { + err := w.Connection.WriteControl(handler.MessageType, + []byte(msg), + time.Now().Add(handler.Delay)) + if err == websocket.ErrCloseSent { + return nil + } else if e, ok := err.(net.Error); ok && e.Temporary() { + return nil + } + return err + } + w.Connection.SetPingHandler(h) + return + } + w.Wg.Add(1) + defer w.Wg.Done() + go func() { + ticker := time.NewTicker(handler.Delay) + for { + select { + case <-w.ShutdownC: + ticker.Stop() + return + case <-ticker.C: + err := w.SendRawMessage(handler.MessageType, handler.Message) + if err != nil { + log.Errorf(log.WebsocketMgr, + "%v websocket connection: ping handler failed to send message [%s]", + w.ExchangeName, + handler.Message) + return + } + } + } + }() +} + +func (w *WebsocketConnection) setConnectedStatus(b bool) { + if b { + atomic.StoreInt32(&w.connected, 1) + return + } + atomic.StoreInt32(&w.connected, 0) +} + +// IsConnected exposes websocket connection status +func (w *WebsocketConnection) IsConnected() bool { + return atomic.LoadInt32(&w.connected) == 1 +} + +// ReadMessage reads messages, can handle text, gzip and binary +func (w *WebsocketConnection) ReadMessage() Response { + mType, resp, err := w.Connection.ReadMessage() + if err != nil { + if isDisconnectionError(err) { + w.setConnectedStatus(false) + w.readMessageErrors <- err + } + return Response{} + } + + select { + case w.Traffic <- struct{}{}: + default: // causes contention, just bypass if there is no receiver. + } + + var standardMessage []byte + switch mType { + case websocket.TextMessage: + standardMessage = resp + case websocket.BinaryMessage: + standardMessage, err = w.parseBinaryResponse(resp) + if err != nil { + log.Errorf(log.WebsocketMgr, + "%v websocket connection: parseBinaryResponse error: %v", + w.ExchangeName, + err) + return Response{} + } + } + if w.Verbose { + log.Debugf(log.WebsocketMgr, + "%v websocket connection: message received: %v", + w.ExchangeName, + string(standardMessage)) + } + return Response{Raw: standardMessage, Type: mType} +} + +// parseBinaryResponse parses a websocket binary response into a usable byte array +func (w *WebsocketConnection) parseBinaryResponse(resp []byte) ([]byte, error) { + var standardMessage []byte + var err error + // Detect GZIP + if resp[0] == 31 && resp[1] == 139 { + b := bytes.NewReader(resp) + var gReader *gzip.Reader + gReader, err = gzip.NewReader(b) + if err != nil { + return standardMessage, err + } + standardMessage, err = ioutil.ReadAll(gReader) + if err != nil { + return standardMessage, err + } + err = gReader.Close() + if err != nil { + return standardMessage, err + } + } else { + reader := flate.NewReader(bytes.NewReader(resp)) + standardMessage, err = ioutil.ReadAll(reader) + if err != nil { + return standardMessage, err + } + err = reader.Close() + if err != nil { + return standardMessage, err + } + } + return standardMessage, nil +} + +// GenerateMessageID Creates a messageID to checkout +func (w *WebsocketConnection) GenerateMessageID(highPrec bool) int64 { + var min int64 = 1e8 + var max int64 = 2e8 + if highPrec { + max = 2e12 + min = 1e12 + } + // utlization of hard coded positive numbers and default crypto/rand + // io.reader will panic on error instead of returning + randomNumber, err := rand.Int(rand.Reader, big.NewInt(max-min+1)) + if err != nil { + panic(err) + } + return randomNumber.Int64() + min +} + +// Shutdown shuts down and closes specific connection +func (w *WebsocketConnection) Shutdown() error { + if w == nil || w.Connection == nil { + return nil + } + return w.Connection.UnderlyingConn().Close() +} + +// SetURL sets connection URL +func (w *WebsocketConnection) SetURL(url string) { + w.URL = url +} + +// SetProxy sets connection proxy +func (w *WebsocketConnection) SetProxy(proxy string) { + w.ProxyURL = proxy +} + +// GetURL returns the connection URL +func (w *WebsocketConnection) GetURL() string { + return w.URL +} diff --git a/exchanges/stream/websocket_test.go b/exchanges/stream/websocket_test.go new file mode 100644 index 00000000..995115e7 --- /dev/null +++ b/exchanges/stream/websocket_test.go @@ -0,0 +1,957 @@ +package stream + +import ( + "bytes" + "compress/flate" + "compress/gzip" + "encoding/json" + "errors" + "fmt" + "net" + "net/http" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" +) + +const ( + websocketTestURL = "wss://www.bitmex.com/realtime" + useProxyTests = false // Disabled by default. Freely available proxy servers that work all the time are difficult to find + proxyURL = "http://212.186.171.4:80" // Replace with a usable proxy server +) + +var dialer websocket.Dialer + +type testStruct struct { + Error error + WC WebsocketConnection +} + +type testRequest struct { + Event string `json:"event"` + RequestID int64 `json:"reqid,omitempty"` + Pairs []string `json:"pair"` + Subscription testRequestData `json:"subscription,omitempty"` +} + +// testRequestData contains details on WS channel +type testRequestData struct { + Name string `json:"name,omitempty"` + Interval int64 `json:"interval,omitempty"` + Depth int64 `json:"depth,omitempty"` +} + +type testResponse struct { + RequestID int64 `json:"reqid,omitempty"` +} + +var defaultSetup = &WebsocketSetup{ + Enabled: true, + AuthenticatedWebsocketAPISupport: true, + WebsocketTimeout: time.Second * 5, + DefaultURL: "testDefaultURL", + ExchangeName: "exchangeName", + RunningURL: "wss://testRunningURL", + Connector: func() error { return nil }, + Subscriber: func(_ []ChannelSubscription) error { return nil }, + UnSubscriber: func(_ []ChannelSubscription) error { return nil }, + GenerateSubscriptions: func() ([]ChannelSubscription, error) { + return []ChannelSubscription{ + {Channel: "TestSub"}, + {Channel: "TestSub2"}, + {Channel: "TestSub3"}, + {Channel: "TestSub4"}, + }, nil + }, + Features: &protocol.Features{Subscribe: true, Unsubscribe: true}, +} + +func TestTrafficMonitorTimeout(t *testing.T) { + ws := *New() + err := ws.Setup(defaultSetup) + if err != nil { + t.Fatal(err) + } + ws.trafficTimeout = time.Second + ws.ShutdownC = make(chan struct{}) + err = ws.trafficMonitor() + if err != nil { + t.Fatal(err) + } + // try to add another traffic monitor + err = ws.trafficMonitor() + if err == nil { + t.Fatal("expected not allowed") + } + // Deploy traffic alert + ws.TrafficAlert <- struct{}{} + time.Sleep(time.Second * 2) + ws.Wg.Wait() + if ws.IsTrafficMonitorRunning() { + t.Error("should be ded") + } +} + +func TestIsDisconnectionError(t *testing.T) { + isADisconnectionError := isDisconnectionError(errors.New("errorText")) + if isADisconnectionError { + t.Error("Its not") + } + isADisconnectionError = isDisconnectionError(&websocket.CloseError{ + Code: 1006, + Text: "errorText", + }) + if !isADisconnectionError { + t.Error("It is") + } + + isADisconnectionError = isDisconnectionError(&net.OpError{ + Op: "", + Net: "", + Source: nil, + Addr: nil, + Err: errors.New("errorText"), + }) + if !isADisconnectionError { + t.Error("It is") + } +} + +func TestConnectionMessageErrors(t *testing.T) { + ws := *New() + err := ws.Setup(defaultSetup) + if err != nil { + t.Fatal(err) + } + ws.trafficTimeout = time.Minute + ws.connector = func() error { return nil } + + err = ws.Connect() + if err != nil { + t.Fatal(err) + } + + ws.TrafficAlert <- struct{}{} + + timer := time.NewTimer(900 * time.Millisecond) + ws.ReadMessageErrors <- errors.New("errorText") + select { + case err := <-ws.ToRoutine: + if err.(error).Error() != "errorText" { + t.Errorf("Expected 'errorText', received %v", err) + } + case <-timer.C: + t.Error("Timeout waiting for datahandler to receive error") + } + ws.ReadMessageErrors <- &websocket.CloseError{ + Code: 1006, + Text: "errorText", + } +outer: + for { + select { + case <-ws.ToRoutine: + t.Fatal("Error is a disconnection error") + case <-timer.C: + break outer + } + } +} + +func TestWebsocket(t *testing.T) { + ws := Websocket{} + err := ws.Setup(&WebsocketSetup{ + ExchangeName: "test", + Enabled: true, + }) + if err != nil && err.Error() != "test Websocket already initialised" { + t.Errorf("Expected 'test Websocket already initialised', received %v", err) + } + + ws = *New() + err = ws.SetProxyAddress("garbagio") + if err == nil { + t.Error("error cannot be nil") + } + err = ws.SetProxyAddress("https://192.168.0.1:1337") + if err != nil { + t.Error("SetProxyAddress", err) + } + // removing proxy + err = ws.SetProxyAddress("") + if err != nil { + t.Error(err) + } + // reinstate proxy + err = ws.SetProxyAddress("http://localhost:1337") + if err != nil { + t.Error(err) + } + // conflict proxy + err = ws.SetProxyAddress("http://localhost:1337") + if err == nil { + t.Error("error cannot be nil") + } + err = ws.Setup(defaultSetup) + if err != nil { + t.Fatal(err) + } + if ws.GetName() != "exchangeName" { + t.Error("WebsocketSetup") + } + + if !ws.IsEnabled() { + t.Error("WebsocketSetup") + } + + ws.setEnabled(false) + if ws.IsEnabled() { + t.Error("WebsocketSetup") + } + ws.setEnabled(true) + if !ws.IsEnabled() { + t.Error("WebsocketSetup") + } + + if ws.GetProxyAddress() != "http://localhost:1337" { + t.Error("WebsocketSetup") + } + + if ws.GetWebsocketURL() != "wss://testRunningURL" { + t.Error("WebsocketSetup") + } + if ws.trafficTimeout != time.Second*5 { + t.Error("WebsocketSetup") + } + // -- Not connected shutdown + err = ws.Shutdown() + if err == nil { + t.Fatal("should not be connected to able to shut down") + } + // -- Normal connect + err = ws.Connect() + if err != nil { + t.Fatal("WebsocketSetup", err) + } + err = ws.SetWebsocketURL("ws://demos.kaazing.com/echo", false, false) + if err != nil { + t.Fatal(err) + } + err = ws.SetWebsocketURL("ws://demos.kaazing.com/echo", true, false) + if err != nil { + t.Fatal(err) + } + // -- Already connected connect + err = ws.Connect() + if err == nil { + t.Fatal("should not connect, already connected") + } + // -- Normal shutdown + err = ws.Shutdown() + if err != nil { + t.Fatal("WebsocketSetup", err) + } + ws.Wg.Wait() +} + +// TestSubscribe logic test +func TestSubscribeUnsubscribe(t *testing.T) { + ws := *New() + err := ws.Setup(defaultSetup) + if err != nil { + t.Fatal(err) + } + + fnSub := func(subs []ChannelSubscription) error { + ws.AddSuccessfulSubscriptions(subs...) + return nil + } + fnUnsub := func(unsubs []ChannelSubscription) error { + ws.RemoveSuccessfulUnsubscriptions(unsubs...) + return nil + } + ws.Subscriber = fnSub + ws.Unsubscriber = fnUnsub + + err = ws.UnsubscribeChannels(nil) + if err == nil { + t.Fatal("error cannot be nil") + } + + // Generate test sub + subs, err := ws.GenerateSubs() + if err != nil { + t.Fatal(err) + } + + // unsub when no subscribed channel + err = ws.UnsubscribeChannels(subs) + if err == nil { + t.Fatal("error cannot be nil") + } + + err = ws.SubscribeToChannels(subs) + if err != nil { + t.Fatal(err) + } + + // subscribe when already subscribed + err = ws.SubscribeToChannels(subs) + if err == nil { + t.Fatal("error cannot be nil") + } + + err = ws.UnsubscribeChannels(subs) + if err != nil { + t.Fatal(err) + } +} + +func TestResubscribe(t *testing.T) { + ws := *New() + err := ws.Setup(defaultSetup) + if err != nil { + t.Fatal(err) + } + + fnSub := func(subs []ChannelSubscription) error { + ws.AddSuccessfulSubscriptions(subs...) + return nil + } + fnUnsub := func(unsubs []ChannelSubscription) error { + ws.RemoveSuccessfulUnsubscriptions(unsubs...) + return nil + } + ws.Subscriber = fnSub + ws.Unsubscriber = fnUnsub + + channel := []ChannelSubscription{{Channel: "resubTest"}} + err = ws.ResubscribeToChannel(&channel[0]) + if err == nil { + t.Fatal("error cannot be nil") + } + + err = ws.SubscribeToChannels(channel) + if err != nil { + t.Fatal(err) + } + + err = ws.ResubscribeToChannel(&channel[0]) + if err != nil { + t.Fatal("error cannot be nil") + } +} + +// TestConnectionMonitorNoConnection logic test +func TestConnectionMonitorNoConnection(t *testing.T) { + ws := *New() + ws.DataHandler = make(chan interface{}, 1) + ws.ShutdownC = make(chan struct{}, 1) + ws.exchangeName = "hello" + ws.trafficTimeout = 1 + go ws.connectionMonitor() + time.Sleep(time.Second) + if ws.IsConnectionMonitorRunning() { + t.Fatal("Should have exited") + } +} + +// TestSliceCopyDoesntImpactBoth logic test +func TestGetSubscriptions(t *testing.T) { + w := Websocket{ + subscriptions: []ChannelSubscription{ + { + Channel: "hello3", + }, + }, + } + if !strings.EqualFold("hello3", w.GetSubscriptions()[0].Channel) { + t.Error("Subscriptions was not copied properly") + } +} + +// TestSetCanUseAuthenticatedEndpoints logic test +func TestSetCanUseAuthenticatedEndpoints(t *testing.T) { + ws := *New() + result := ws.CanUseAuthenticatedEndpoints() + if result { + t.Error("expected `canUseAuthenticatedEndpoints` to be false") + } + ws.SetCanUseAuthenticatedEndpoints(true) + result = ws.CanUseAuthenticatedEndpoints() + if !result { + t.Error("expected `canUseAuthenticatedEndpoints` to be true") + } +} + +// TestDial logic test +func TestDial(t *testing.T) { + var testCases = []testStruct{ + {Error: nil, + WC: WebsocketConnection{ + ExchangeName: "test1", + Verbose: true, + URL: websocketTestURL, + RateLimit: 10, + ResponseMaxLimit: 7000000000, + }, + }, + {Error: errors.New(" Error: malformed ws or wss URL"), + WC: WebsocketConnection{ + ExchangeName: "test2", + Verbose: true, + URL: "", + ResponseMaxLimit: 7000000000, + }, + }, + {Error: nil, + WC: WebsocketConnection{ + ExchangeName: "test3", + Verbose: true, + URL: websocketTestURL, + ProxyURL: proxyURL, + ResponseMaxLimit: 7000000000, + }, + }, + } + for i := range testCases { + testData := &testCases[i] + t.Run(testData.WC.ExchangeName, func(t *testing.T) { + if testData.WC.ProxyURL != "" && !useProxyTests { + t.Skip("Proxy testing not enabled, skipping") + } + err := testData.WC.Dial(&dialer, http.Header{}) + if err != nil { + if testData.Error != nil && strings.Contains(err.Error(), testData.Error.Error()) { + return + } + t.Fatal(err) + } + }) + } +} + +// TestSendMessage logic test +func TestSendMessage(t *testing.T) { + var testCases = []testStruct{ + {Error: nil, WC: WebsocketConnection{ + ExchangeName: "test1", + Verbose: true, + URL: websocketTestURL, + RateLimit: 10, + ResponseMaxLimit: 7000000000, + }, + }, + {Error: errors.New(" Error: malformed ws or wss URL"), + WC: WebsocketConnection{ + ExchangeName: "test2", + Verbose: true, + URL: "", + ResponseMaxLimit: 7000000000, + }, + }, + {Error: nil, + WC: WebsocketConnection{ + ExchangeName: "test3", + Verbose: true, + URL: websocketTestURL, + ProxyURL: proxyURL, + ResponseMaxLimit: 7000000000, + }, + }, + } + for i := range testCases { + testData := &testCases[i] + t.Run(testData.WC.ExchangeName, func(t *testing.T) { + if testData.WC.ProxyURL != "" && !useProxyTests { + t.Skip("Proxy testing not enabled, skipping") + } + err := testData.WC.Dial(&dialer, http.Header{}) + if err != nil { + if testData.Error != nil && strings.Contains(err.Error(), testData.Error.Error()) { + return + } + t.Fatal(err) + } + err = testData.WC.SendJSONMessage(Ping) + if err != nil { + t.Error(err) + } + err = testData.WC.SendRawMessage(websocket.TextMessage, []byte(Ping)) + if err != nil { + t.Error(err) + } + }) + } +} + +// TestSendMessageWithResponse logic test +func TestSendMessageWithResponse(t *testing.T) { + wc := &WebsocketConnection{ + Verbose: true, + URL: "wss://echo.websocket.org", + ResponseMaxLimit: time.Second * 5, + Match: NewMatch(), + } + if wc.ProxyURL != "" && !useProxyTests { + t.Skip("Proxy testing not enabled, skipping") + } + + err := wc.Dial(&dialer, http.Header{}) + if err != nil { + t.Fatal(err) + } + + go readMessages(wc, t) + + request := testRequest{ + Event: "subscribe", + Pairs: []string{currency.NewPairWithDelimiter("XBT", "USD", "/").String()}, + Subscription: testRequestData{ + Name: "ticker", + }, + RequestID: wc.GenerateMessageID(false), + } + + _, err = wc.SendMessageReturnResponse(request.RequestID, request) + if err != nil { + t.Error(err) + } +} + +// readMessages helper func +func readMessages(wc *WebsocketConnection, t *testing.T) { + timer := time.NewTimer(20 * time.Second) + for { + select { + case <-timer.C: + return + default: + resp := wc.ReadMessage() + if resp.Raw == nil { + t.Error("connection has closed") + return + } + var incoming testResponse + err := json.Unmarshal(resp.Raw, &incoming) + if err != nil { + t.Error(err) + return + } + if incoming.RequestID > 0 { + wc.Match.IncomingWithData(incoming.RequestID, resp.Raw) + return + } + } + } +} + +// TestSetupPingHandler logic test +func TestSetupPingHandler(t *testing.T) { + wc := &WebsocketConnection{ + URL: "wss://echo.websocket.org", + ResponseMaxLimit: time.Second * 5, + Match: NewMatch(), + Wg: &sync.WaitGroup{}, + } + + if wc.ProxyURL != "" && !useProxyTests { + t.Skip("Proxy testing not enabled, skipping") + } + wc.ShutdownC = make(chan struct{}) + err := wc.Dial(&dialer, http.Header{}) + if err != nil { + t.Fatal(err) + } + + wc.SetupPingHandler(PingHandler{ + UseGorillaHandler: true, + MessageType: websocket.PingMessage, + Delay: 1000, + }) + + err = wc.Connection.Close() + if err != nil { + t.Error(err) + } + + err = wc.Dial(&dialer, http.Header{}) + if err != nil { + t.Fatal(err) + } + wc.SetupPingHandler(PingHandler{ + MessageType: websocket.TextMessage, + Message: []byte(Ping), + Delay: 200, + }) + time.Sleep(time.Millisecond * 500) + close(wc.ShutdownC) + wc.Wg.Wait() +} + +// TestParseBinaryResponse logic test +func TestParseBinaryResponse(t *testing.T) { + wc := &WebsocketConnection{ + URL: "wss://echo.websocket.org", + ResponseMaxLimit: time.Second * 5, + Match: NewMatch(), + } + + var b bytes.Buffer + w := gzip.NewWriter(&b) + _, err := w.Write([]byte("hello")) + if err != nil { + t.Error(err) + } + err = w.Close() + if err != nil { + t.Error(err) + } + var resp []byte + resp, err = wc.parseBinaryResponse(b.Bytes()) + if err != nil { + t.Error(err) + } + if !strings.EqualFold(string(resp), "hello") { + t.Errorf("GZip conversion failed. Received: '%v', Expected: 'hello'", string(resp)) + } + + var b2 bytes.Buffer + w2, err2 := flate.NewWriter(&b2, 1) + if err2 != nil { + t.Error(err2) + } + _, err2 = w2.Write([]byte("hello")) + if err2 != nil { + t.Error(err) + } + err2 = w2.Close() + if err2 != nil { + t.Error(err) + } + resp2, err3 := wc.parseBinaryResponse(b2.Bytes()) + if err3 != nil { + t.Error(err3) + } + if !strings.EqualFold(string(resp2), "hello") { + t.Errorf("GZip conversion failed. Received: '%v', Expected: 'hello'", string(resp2)) + } +} + +// TestCanUseAuthenticatedWebsocketForWrapper logic test +func TestCanUseAuthenticatedWebsocketForWrapper(t *testing.T) { + ws := &Websocket{} + resp := ws.CanUseAuthenticatedWebsocketForWrapper() + if resp { + t.Error("Expected false, `connected` is false") + } + ws.setConnectedStatus(true) + resp = ws.CanUseAuthenticatedWebsocketForWrapper() + if resp { + t.Error("Expected false, `connected` is true and `CanUseAuthenticatedEndpoints` is false") + } + ws.canUseAuthenticatedEndpoints = true + resp = ws.CanUseAuthenticatedWebsocketForWrapper() + if !resp { + t.Error("Expected true, `connected` and `CanUseAuthenticatedEndpoints` is true") + } +} + +func TestGenerateMessageID(t *testing.T) { + wc := WebsocketConnection{} + var id int64 + for i := 0; i < 10; i++ { + newID := wc.GenerateMessageID(true) + if id == newID { + t.Fatal("ID generation is not unique") + } + id = newID + } +} + +// BenchmarkGenerateMessageID-8 2850018 408 ns/op 56 B/op 4 allocs/op +func BenchmarkGenerateMessageID_High(b *testing.B) { + wc := WebsocketConnection{} + for i := 0; i < b.N; i++ { + _ = wc.GenerateMessageID(true) + } +} + +// BenchmarkGenerateMessageID_Low-8 2591596 447 ns/op 56 B/op 4 allocs/op +func BenchmarkGenerateMessageID_Low(b *testing.B) { + wc := WebsocketConnection{} + for i := 0; i < b.N; i++ { + _ = wc.GenerateMessageID(false) + } +} + +func TestCheckWebsocketURL(t *testing.T) { + err := checkWebsocketURL("") + if err == nil { + t.Fatal("error cannot be nil") + } + + err = checkWebsocketURL("wowowow:wowowowo") + if err == nil { + t.Fatal("error cannot be nil") + } + + err = checkWebsocketURL("://") + if err == nil { + t.Fatal("error cannot be nil") + } + + err = checkWebsocketURL("http://www.google.com") + if err == nil { + t.Fatal("error cannot be nil") + } + + err = checkWebsocketURL("wss://websocketconnection.place") + if err != nil { + t.Fatal(err) + } + + err = checkWebsocketURL("ws://websocketconnection.place") + if err != nil { + t.Fatal(err) + } +} + +func TestGetChannelDifference(t *testing.T) { + web := Websocket{} + + newChans := []ChannelSubscription{ + { + Channel: "Test1", + }, + { + Channel: "Test2", + }, + { + Channel: "Test3", + }, + } + subs, unsubs := web.GetChannelDifference(newChans) + if len(subs) != 3 { + t.Fatal("error mismatch") + } + + if len(unsubs) != 0 { + t.Fatal("error mismatch") + } + + web.subscriptions = subs + + flushedSubs := []ChannelSubscription{ + { + Channel: "Test2", + }, + } + + subs, unsubs = web.GetChannelDifference(flushedSubs) + if len(subs) != 0 { + t.Fatal("error mismatch") + } + if len(unsubs) != 2 { + t.Fatal("error mismatch") + } + + flushedSubs = []ChannelSubscription{ + { + Channel: "Test2", + }, + { + Channel: "Test4", + }, + } + + subs, unsubs = web.GetChannelDifference(flushedSubs) + if len(subs) != 1 { + t.Fatal("error mismatch") + } + if len(unsubs) != 2 { + t.Fatal("error mismatch") + } +} + +// GenSubs defines a theoretical exchange with pair management +type GenSubs struct { + EnabledPairs currency.Pairs + subscribos []ChannelSubscription + unsubscribos []ChannelSubscription +} + +// generateSubs default subs created from the enabled pairs list +func (g *GenSubs) generateSubs() ([]ChannelSubscription, error) { + var superduperchannelsubs []ChannelSubscription + for i := range g.EnabledPairs { + superduperchannelsubs = append(superduperchannelsubs, ChannelSubscription{ + Channel: "TEST:" + strconv.FormatInt(int64(i), 10), + Currency: g.EnabledPairs[i], + }) + } + return superduperchannelsubs, nil +} + +func (g *GenSubs) SUBME(subs []ChannelSubscription) error { + if len(subs) == 0 { + return errors.New("WOW") + } + g.subscribos = subs + return nil +} + +func (g *GenSubs) UNSUBME(unsubs []ChannelSubscription) error { + if len(unsubs) == 0 { + return errors.New("WOW") + } + g.unsubscribos = unsubs + return nil +} + +// sneaky connect func +func connect() error { return nil } + +func TestFlushChannels(t *testing.T) { + // Enabled pairs/setup system + newgen := GenSubs{EnabledPairs: []currency.Pair{ + currency.NewPair(currency.BTC, currency.AUD), + currency.NewPair(currency.BTC, currency.USDT), + }} + web := Websocket{enabled: true, + connected: true, + connector: connect, + ShutdownC: make(chan struct{}), + Subscriber: newgen.SUBME, + Unsubscriber: newgen.UNSUBME, + Wg: new(sync.WaitGroup), + features: &protocol.Features{ + // No features + }} + web.GenerateSubs = newgen.generateSubs + subs, err := web.GenerateSubs() + if err != nil { + t.Fatal(err) + } + web.subscriptions = subs + // Disable pair and flush system + newgen.EnabledPairs = []currency.Pair{ + currency.NewPair(currency.BTC, currency.AUD)} + err = web.FlushChannels() + if err != nil { + t.Fatal(err) + } + + web.features.FullPayloadSubscribe = true + err = web.FlushChannels() + if err != nil { + t.Fatal(err) + } + web.features.FullPayloadSubscribe = false + web.features.Subscribe = true + err = web.FlushChannels() + if err != nil { + t.Fatal(err) + } + web.setConnectedStatus(true) + web.features.Unsubscribe = true + err = web.FlushChannels() + if err != nil { + t.Fatal(err) + } +} + +func TestDisable(t *testing.T) { + web := Websocket{ + enabled: true, + connected: true, + ShutdownC: make(chan struct{}), + } + err := web.Disable() + if err != nil { + t.Fatal(err) + } + err = web.Disable() + if err == nil { + t.Fatal("should already be disabled") + } +} + +func TestEnable(t *testing.T) { + web := Websocket{ + connector: connect, + Wg: new(sync.WaitGroup), + ShutdownC: make(chan struct{}), + } + err := web.Enable() + if err != nil { + t.Fatal(err) + } + + err = web.Enable() + if err == nil { + t.Fatal("should already be enabled") + } + + fmt.Print() +} + +func TestSetupNewConnection(t *testing.T) { + web := Websocket{ + connector: connect, + Wg: new(sync.WaitGroup), + ShutdownC: make(chan struct{}), + Init: true, + TrafficAlert: make(chan struct{}), + ReadMessageErrors: make(chan error), + } + + err := web.Setup(defaultSetup) + if err != nil { + t.Fatal(err) + } + err = web.SetupNewConnection(ConnectionSetup{}) + if err == nil { + t.Fatal("error cannot be nil") + } + err = web.SetupNewConnection(ConnectionSetup{URL: "urlstring"}) + if err != nil { + t.Fatal(err) + } + err = web.SetupNewConnection(ConnectionSetup{URL: "urlstring", + Authenticated: true}) + if err != nil { + t.Fatal(err) + } +} + +func TestWebsocketConnectionShutdown(t *testing.T) { + wc := WebsocketConnection{} + err := wc.Shutdown() + if err != nil { + t.Fatal(err) + } + + err = wc.Dial(&websocket.Dialer{}, nil) + if err == nil { + t.Fatal("error cannot be nil") + } + + wc.URL = "wss://echo.websocket.org" + + err = wc.Dial(&websocket.Dialer{}, nil) + if err != nil { + t.Fatal(err) + } + + err = wc.Shutdown() + if err != nil { + t.Fatal(err) + } +} diff --git a/exchanges/stream/websocket_types.go b/exchanges/stream/websocket_types.go new file mode 100644 index 00000000..06f08518 --- /dev/null +++ b/exchanges/stream/websocket_types.go @@ -0,0 +1,132 @@ +package stream + +import ( + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream/buffer" +) + +// Websocket functionality list and state consts +const ( + // WebsocketNotEnabled alerts of a disabled websocket + WebsocketNotEnabled = "exchange_websocket_not_enabled" + // connection monitor time delays and limits + connectionMonitorDelay = 2 * time.Second + WebsocketNotAuthenticatedUsingRest = "%v - Websocket not authenticated, using REST\n" + Ping = "ping" + Pong = "pong" + UnhandledMessage = " - Unhandled websocket message: " +) + +// Websocket defines a return type for websocket connections via the interface +// wrapper for routine processing in routines.go +type Websocket struct { + canUseAuthenticatedEndpoints bool + enabled bool + Init bool + connected bool + connecting bool + verbose bool + connectionMonitorRunning bool + trafficMonitorRunning bool + dataMonitorRunning bool + trafficTimeout time.Duration + proxyAddr string + defaultURL string + defaultURLAuth string + runningURL string + runningURLAuth string + exchangeName string + m sync.Mutex + connectionMutex sync.RWMutex + connector func() error + + subscriptionMutex sync.Mutex + subscriptions []ChannelSubscription + Subscribe chan []ChannelSubscription + Unsubscribe chan []ChannelSubscription + + // Subscriber function for package defined websocket subscriber + // functionality + Subscriber func([]ChannelSubscription) error + // Unsubscriber function for packaged defined websocket unsubscriber + // functionality + Unsubscriber func([]ChannelSubscription) error + // GenerateSubs function for package defined websocket generate + // subscriptions functionality + GenerateSubs func() ([]ChannelSubscription, error) + + DataHandler chan interface{} + ToRoutine chan interface{} + + Match *Match + + // shutdown synchronises shutdown event across routines + ShutdownC chan struct{} + Wg *sync.WaitGroup + + // Orderbook is a local buffer of orderbooks + Orderbook buffer.Orderbook + + // trafficAlert monitors if there is a halt in traffic throughput + TrafficAlert chan struct{} + // ReadMessageErrors will received all errors from ws.ReadMessage() and + // verify if its a disconnection + ReadMessageErrors chan error + features *protocol.Features + + // Standard stream connection + Conn Connection + // Authenticated stream connection + AuthConn Connection +} + +// WebsocketSetup defines variables for setting up a websocket connection +type WebsocketSetup struct { + Enabled bool + Verbose bool + AuthenticatedWebsocketAPISupport bool + WebsocketTimeout time.Duration + DefaultURL string + ExchangeName string + RunningURL string + RunningURLAuth string + Connector func() error + Subscriber func([]ChannelSubscription) error + UnSubscriber func([]ChannelSubscription) error + GenerateSubscriptions func() ([]ChannelSubscription, error) + Features *protocol.Features + // Local orderbook buffer config values + OrderbookBufferLimit int + BufferEnabled bool + SortBuffer bool + SortBufferByUpdateIDs bool + UpdateEntriesByID bool +} + +// WebsocketConnection contains all the data needed to send a message to a WS +// connection +type WebsocketConnection struct { + Verbose bool + connected int32 + + // Gorilla websocket does not allow more than one goroutine to utilise + // writes methods + writeControl sync.Mutex + + RateLimit int64 + ExchangeName string + URL string + ProxyURL string + Wg *sync.WaitGroup + Connection *websocket.Conn + ShutdownC chan struct{} + + Match *Match + ResponseMaxLimit time.Duration + Traffic chan struct{} + readMessageErrors chan error +} diff --git a/exchanges/ticker/ticker.go b/exchanges/ticker/ticker.go index ea418b84..7f7947cc 100644 --- a/exchanges/ticker/ticker.go +++ b/exchanges/ticker/ticker.go @@ -33,7 +33,6 @@ func SubscribeTicker(exchange string, p currency.Pair, a asset.Item) (dispatch.P p, a) } - return service.mux.Subscribe(tick.Main) } @@ -80,25 +79,22 @@ func GetTicker(exchange string, p currency.Pair, tickerType asset.Item) (*Price, // ProcessTicker processes incoming tickers, creating or updating the Tickers // list -func ProcessTicker(exchangeName string, tickerNew *Price, assetType asset.Item) error { - if exchangeName == "" { +func ProcessTicker(tickerNew *Price) error { + if tickerNew.ExchangeName == "" { return fmt.Errorf(errExchangeNameUnset) } - tickerNew.ExchangeName = strings.ToLower(exchangeName) - if tickerNew.Pair.IsEmpty() { - return fmt.Errorf("%s %s", exchangeName, errPairNotSet) + return fmt.Errorf("%s %s", tickerNew.ExchangeName, errPairNotSet) } - if assetType == "" { - return fmt.Errorf("%s %s %s", exchangeName, + if tickerNew.AssetType == "" { + return fmt.Errorf("%s %s %s", + tickerNew.ExchangeName, tickerNew.Pair, errAssetTypeNotSet) } - tickerNew.AssetType = assetType - if tickerNew.LastUpdated.IsZero() { tickerNew.LastUpdated = time.Now() } @@ -108,46 +104,11 @@ func ProcessTicker(exchangeName string, tickerNew *Price, assetType asset.Item) // Update updates ticker price func (s *Service) Update(p *Price) error { - var ids []uuid.UUID - + name := strings.ToLower(p.ExchangeName) s.Lock() - switch { - case s.Tickers[p.ExchangeName] == nil: - s.Tickers[p.ExchangeName] = make(map[*currency.Item]map[*currency.Item]map[asset.Item]*Ticker) - s.Tickers[p.ExchangeName][p.Pair.Base.Item] = make(map[*currency.Item]map[asset.Item]*Ticker) - s.Tickers[p.ExchangeName][p.Pair.Base.Item][p.Pair.Quote.Item] = make(map[asset.Item]*Ticker) - err := s.SetItemID(p) - if err != nil { - s.Unlock() - return err - } - case s.Tickers[p.ExchangeName][p.Pair.Base.Item] == nil: - s.Tickers[p.ExchangeName][p.Pair.Base.Item] = make(map[*currency.Item]map[asset.Item]*Ticker) - s.Tickers[p.ExchangeName][p.Pair.Base.Item][p.Pair.Quote.Item] = make(map[asset.Item]*Ticker) - err := s.SetItemID(p) - if err != nil { - s.Unlock() - return err - } - - case s.Tickers[p.ExchangeName][p.Pair.Base.Item][p.Pair.Quote.Item] == nil: - s.Tickers[p.ExchangeName][p.Pair.Base.Item][p.Pair.Quote.Item] = make(map[asset.Item]*Ticker) - err := s.SetItemID(p) - if err != nil { - s.Unlock() - return err - } - - case s.Tickers[p.ExchangeName][p.Pair.Base.Item][p.Pair.Quote.Item][p.AssetType] == nil: - err := s.SetItemID(p) - if err != nil { - s.Unlock() - return err - } - - default: - ticker := s.Tickers[p.ExchangeName][p.Pair.Base.Item][p.Pair.Quote.Item][p.AssetType] + ticker, ok := s.Tickers[name][p.Pair.Base.Item][p.Pair.Quote.Item][p.AssetType] + if ok { ticker.Last = p.Last ticker.High = p.High ticker.Low = p.Low @@ -159,20 +120,39 @@ func (s *Service) Update(p *Price) error { ticker.Open = p.Open ticker.Close = p.Close ticker.LastUpdated = p.LastUpdated - ids = ticker.Assoc - ids = append(ids, ticker.Main) + ids := append(ticker.Assoc, ticker.Main) + s.Unlock() + return s.mux.Publish(ids, p) } + + switch { + case s.Tickers[name] == nil: + s.Tickers[name] = make(map[*currency.Item]map[*currency.Item]map[asset.Item]*Ticker) + fallthrough + case s.Tickers[name][p.Pair.Base.Item] == nil: + s.Tickers[name][p.Pair.Base.Item] = make(map[*currency.Item]map[asset.Item]*Ticker) + fallthrough + case s.Tickers[name][p.Pair.Base.Item][p.Pair.Quote.Item] == nil: + s.Tickers[name][p.Pair.Base.Item][p.Pair.Quote.Item] = make(map[asset.Item]*Ticker) + } + + err := s.SetItemID(p, name) + if err != nil { + s.Unlock() + return err + } + s.Unlock() - return s.mux.Publish(ids, p) + return nil } // SetItemID retrieves and sets dispatch mux publish IDs -func (s *Service) SetItemID(p *Price) error { +func (s *Service) SetItemID(p *Price, fmtName string) error { if p == nil { return errors.New(errTickerPriceIsNil) } - ids, err := s.GetAssociations(p) + ids, err := s.GetAssociations(p, fmtName) if err != nil { return err } @@ -181,26 +161,26 @@ func (s *Service) SetItemID(p *Price) error { return err } - s.Tickers[p.ExchangeName][p.Pair.Base.Item][p.Pair.Quote.Item][p.AssetType] = &Ticker{Price: *p, + s.Tickers[fmtName][p.Pair.Base.Item][p.Pair.Quote.Item][p.AssetType] = &Ticker{Price: *p, Main: singleID, Assoc: ids} return nil } // GetAssociations links a singular book with it's dispatch associations -func (s *Service) GetAssociations(p *Price) ([]uuid.UUID, error) { +func (s *Service) GetAssociations(p *Price, fmtName string) ([]uuid.UUID, error) { if p == nil || *p == (Price{}) { return nil, errors.New(errTickerPriceIsNil) } var ids []uuid.UUID - exchangeID, ok := s.Exchange[p.ExchangeName] + exchangeID, ok := s.Exchange[fmtName] if !ok { var err error exchangeID, err = s.mux.GetID() if err != nil { return nil, err } - s.Exchange[p.ExchangeName] = exchangeID + s.Exchange[fmtName] = exchangeID } ids = append(ids, exchangeID) diff --git a/exchanges/ticker/ticker_test.go b/exchanges/ticker/ticker_test.go index a947b33e..feff25d7 100644 --- a/exchanges/ticker/ticker_test.go +++ b/exchanges/ticker/ticker_test.go @@ -37,32 +37,48 @@ func TestSubscribeTicker(t *testing.T) { // force error service.mux = nil - err = ProcessTicker("subscribetest", &Price{Pair: p}, asset.Spot) + err = ProcessTicker(&Price{ + Pair: p, + ExchangeName: "subscribetest", + AssetType: asset.Spot}) if err == nil { t.Error("error cannot be nil") } sillyP := p sillyP.Base = currency.GALA_NEO - err = ProcessTicker("subscribetest", &Price{Pair: sillyP}, asset.Spot) + err = ProcessTicker(&Price{ + Pair: sillyP, + ExchangeName: "subscribetest", + AssetType: asset.Spot}) if err == nil { t.Error("error cannot be nil") } sillyP.Quote = currency.AAA - err = ProcessTicker("subscribetest", &Price{Pair: sillyP}, asset.Spot) + err = ProcessTicker(&Price{ + Pair: sillyP, + ExchangeName: "subscribetest", + AssetType: asset.Spot}) if err == nil { t.Error("error cannot be nil") } - err = ProcessTicker("subscribetest", &Price{Pair: sillyP}, "silly") + err = ProcessTicker(&Price{ + Pair: sillyP, + ExchangeName: "subscribetest", + AssetType: "silly", + }) if err == nil { t.Error("error cannot be nil") } // reinstate mux service.mux = cpyMux - err = ProcessTicker("subscribetest", &Price{Pair: p}, asset.Spot) + err = ProcessTicker(&Price{ + Pair: p, + ExchangeName: "subscribetest", + AssetType: asset.Spot}) if err != nil { t.Error("error cannot be nil") } @@ -81,7 +97,10 @@ func TestSubscribeToExchangeTickers(t *testing.T) { p := currency.NewPair(currency.BTC, currency.USD) - err = ProcessTicker("subscribeExchangeTest", &Price{Pair: p}, asset.Spot) + err = ProcessTicker(&Price{ + Pair: p, + ExchangeName: "subscribeExchangeTest", + AssetType: asset.Spot}) if err != nil { t.Error(err) } @@ -93,19 +112,24 @@ func TestSubscribeToExchangeTickers(t *testing.T) { } func TestGetTicker(t *testing.T) { - newPair := currency.NewPairFromStrings("BTC", "USD") + newPair, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } priceStruct := Price{ - Pair: newPair, - Last: 1200, - High: 1298, - Low: 1148, - Bid: 1195, - Ask: 1220, - Volume: 5, - PriceATH: 1337, + Pair: newPair, + Last: 1200, + High: 1298, + Low: 1148, + Bid: 1195, + Ask: 1220, + Volume: 5, + PriceATH: 1337, + ExchangeName: "bitfinex", + AssetType: asset.Spot, } - err := ProcessTicker("bitfinex", &priceStruct, asset.Spot) + err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) } @@ -129,7 +153,11 @@ func TestGetTicker(t *testing.T) { t.Fatal("TestGetTicker returned ticker for invalid first currency") } - btcltcPair := currency.NewPairFromStrings("BTC", "LTC") + btcltcPair, err := currency.NewPairFromStrings("BTC", "LTC") + if err != nil { + t.Fatal(err) + } + _, err = GetTicker("bitfinex", btcltcPair, asset.Spot) if err == nil { t.Fatal("TestGetTicker returned ticker for invalid second currency") @@ -137,7 +165,8 @@ func TestGetTicker(t *testing.T) { priceStruct.PriceATH = 9001 priceStruct.Pair.Base = currency.ETH - err = ProcessTicker("bitfinex", &priceStruct, "futures_3m") + priceStruct.AssetType = "futures_3m" + err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) } @@ -156,13 +185,14 @@ func TestGetTicker(t *testing.T) { t.Error("Ticker GetTicker error cannot be nil") } - err = ProcessTicker("bitfinex", &priceStruct, "meowCats") + priceStruct.AssetType = "meowCats" + err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) } // process update again - err = ProcessTicker("bitfinex", &priceStruct, "meowCats") + err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) } @@ -170,7 +200,11 @@ func TestGetTicker(t *testing.T) { func TestProcessTicker(t *testing.T) { // non-appending function to tickers exchName := "bitstamp" - newPair := currency.NewPairFromStrings("BTC", "USD") + newPair, err := currency.NewPairFromStrings("BTC", "USD") + if err != nil { + t.Fatal(err) + } + priceStruct := Price{ Last: 1200, High: 1298, @@ -181,26 +215,28 @@ func TestProcessTicker(t *testing.T) { // non-appending function to tickers PriceATH: 1337, } - err := ProcessTicker("", &priceStruct, asset.Spot) + err = ProcessTicker(&priceStruct) if err == nil { t.Fatal("empty exchange should throw an err") } + priceStruct.ExchangeName = exchName + // test for empty pair - err = ProcessTicker(exchName, &priceStruct, asset.Spot) + err = ProcessTicker(&priceStruct) if err == nil { t.Fatal("empty pair should throw an err") } // test for empty asset type priceStruct.Pair = newPair - err = ProcessTicker(exchName, &priceStruct, "") + err = ProcessTicker(&priceStruct) if err == nil { t.Fatal("ProcessTicker error cannot be nil") } - + priceStruct.AssetType = asset.Spot // now process a valid ticker - err = ProcessTicker(exchName, &priceStruct, asset.Spot) + err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) } @@ -213,9 +249,13 @@ func TestProcessTicker(t *testing.T) { // non-appending function to tickers } // now test for processing a pair with a different quote currency - newPair = currency.NewPairFromStrings("BTC", "AUD") + newPair, err = currency.NewPairFromStrings("BTC", "AUD") + if err != nil { + t.Fatal(err) + } + priceStruct.Pair = newPair - err = ProcessTicker(exchName, &priceStruct, asset.Spot) + err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) } @@ -229,9 +269,13 @@ func TestProcessTicker(t *testing.T) { // non-appending function to tickers } // now test for processing a pair which has a different base currency - newPair = currency.NewPairFromStrings("LTC", "AUD") + newPair, err = currency.NewPairFromStrings("LTC", "AUD") + if err != nil { + t.Fatal(err) + } + priceStruct.Pair = newPair - err = ProcessTicker(exchName, &priceStruct, asset.Spot) + err = ProcessTicker(&priceStruct) if err != nil { t.Fatal("ProcessTicker error", err) } @@ -266,16 +310,21 @@ func TestProcessTicker(t *testing.T) { // non-appending function to tickers wg.Add(1) go func() { newName := "Exchange" + strconv.FormatInt(rand.Int63(), 10) - newPairs := currency.NewPairFromStrings("BTC"+strconv.FormatInt(rand.Int63(), 10), + newPairs, err := currency.NewPairFromStrings("BTC"+strconv.FormatInt(rand.Int63(), 10), "USD"+strconv.FormatInt(rand.Int63(), 10)) + if err != nil { + log.Fatal(err) + } tp := Price{ - Pair: newPairs, - Last: rand.Float64(), + Pair: newPairs, + Last: rand.Float64(), + ExchangeName: newName, + AssetType: asset.Spot, } sm.Lock() - err = ProcessTicker(newName, &tp, asset.Spot) + err = ProcessTicker(&tp) if err != nil { t.Error(err) catastrophicFailure = true @@ -319,12 +368,12 @@ func TestProcessTicker(t *testing.T) { // non-appending function to tickers } func TestSetItemID(t *testing.T) { - err := service.SetItemID(nil) + err := service.SetItemID(nil, "") if err == nil { t.Error("error cannot be nil") } - err = service.SetItemID(&Price{}) + err = service.SetItemID(&Price{}, "") if err == nil { t.Error("error cannot be nil") } @@ -332,7 +381,7 @@ func TestSetItemID(t *testing.T) { p := currency.NewPair(currency.CYC, currency.CYG) service.mux = nil - err = service.SetItemID(&Price{Pair: p, ExchangeName: "SetItemID"}) + err = service.SetItemID(&Price{Pair: p, ExchangeName: "SetItemID"}, "setitemid") if err == nil { t.Error("error cannot be nil") } @@ -341,7 +390,7 @@ func TestSetItemID(t *testing.T) { } func TestGetAssociation(t *testing.T) { - _, err := service.GetAssociations(nil) + _, err := service.GetAssociations(nil, "") if err == nil { t.Error("error cannot be nil") } @@ -350,7 +399,7 @@ func TestGetAssociation(t *testing.T) { service.mux = nil - _, err = service.GetAssociations(&Price{Pair: p, ExchangeName: "GetAssociation"}) + _, err = service.GetAssociations(&Price{Pair: p, ExchangeName: "GetAssociation"}, "getassociation") if err == nil { t.Error("error cannot be nil") } diff --git a/exchanges/websocket/wshandler/wshandler.go b/exchanges/websocket/wshandler/wshandler.go deleted file mode 100644 index c29e4a29..00000000 --- a/exchanges/websocket/wshandler/wshandler.go +++ /dev/null @@ -1,898 +0,0 @@ -package wshandler - -import ( - "bytes" - "compress/flate" - "compress/gzip" - "errors" - "fmt" - "io/ioutil" - "net" - "net/http" - "net/url" - "strings" - "sync" - "time" - - "github.com/gorilla/websocket" - "github.com/thrasher-corp/gocryptotrader/config" - "github.com/thrasher-corp/gocryptotrader/log" -) - -// New initialises the websocket struct -func New() *Websocket { - return &Websocket{ - defaultURL: "", - enabled: false, - proxyAddr: "", - runningURL: "", - init: true, - } -} - -// Setup sets main variables for websocket connection -func (w *Websocket) Setup(setupData *WebsocketSetup) error { - w.DataHandler = make(chan interface{}, 1) - w.TrafficAlert = make(chan struct{}, 1) - w.verbose = setupData.Verbose - w.SetChannelSubscriber(setupData.Subscriber) - w.SetChannelUnsubscriber(setupData.UnSubscriber) - w.enabled = setupData.Enabled - w.SetDefaultURL(setupData.DefaultURL) - w.SetConnector(setupData.Connector) - w.SetWebsocketURL(setupData.RunningURL) - w.SetExchangeName(setupData.ExchangeName) - w.SetCanUseAuthenticatedEndpoints(setupData.AuthenticatedWebsocketAPISupport) - w.trafficTimeout = setupData.WebsocketTimeout - w.features = setupData.Features - err := w.Initialise() - if err != nil { - return err - } - - return nil -} - -// Connect initiates a websocket connection by using a package defined connection -// function -func (w *Websocket) Connect() error { - w.m.Lock() - defer w.m.Unlock() - - if !w.IsEnabled() { - return errors.New(WebsocketNotEnabled) - } - if w.IsConnecting() { - return fmt.Errorf("%v Websocket already attempting to connect", - w.exchangeName) - } - if w.IsConnected() { - return fmt.Errorf("%v Websocket already connected", - w.exchangeName) - } - w.setConnectingStatus(true) - w.ShutdownC = make(chan struct{}, 1) - w.ReadMessageErrors = make(chan error, 1) - err := w.connector() - if err != nil { - w.setConnectingStatus(false) - return fmt.Errorf("%v Error connecting %s", - w.exchangeName, err) - } - - w.setConnectedStatus(true) - w.setConnectingStatus(false) - w.setInit(true) - - var anotherWG sync.WaitGroup - anotherWG.Add(1) - go w.trafficMonitor(&anotherWG) - anotherWG.Wait() - if !w.IsConnectionMonitorRunning() { - go w.connectionMonitor() - } - if w.features.Subscribe || w.features.Unsubscribe { - w.Wg.Add(1) - go w.manageSubscriptions() - } - - return nil -} - -// connectionMonitor ensures that the WS keeps connecting -func (w *Websocket) connectionMonitor() { - if w.IsConnectionMonitorRunning() { - return - } - w.setConnectionMonitorRunning(true) - timer := time.NewTimer(connectionMonitorDelay) - - defer func() { - if !timer.Stop() { - select { - case <-timer.C: - default: - } - } - w.setConnectionMonitorRunning(false) - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v websocket connection monitor exiting", - w.exchangeName) - } - }() - - for { - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v running connection monitor cycle", - w.exchangeName) - } - if !w.IsEnabled() { - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v connectionMonitor: websocket disabled, shutting down", w.exchangeName) - } - if w.IsConnected() { - err := w.Shutdown() - if err != nil { - log.Error(log.WebsocketMgr, err) - } - } - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v websocket connection monitor exiting", - w.exchangeName) - } - return - } - select { - case err := <-w.ReadMessageErrors: - // check if this error is a disconnection error - if isDisconnectionError(err) { - w.setConnectedStatus(false) - w.setConnectingStatus(false) - w.setInit(false) - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v websocket has been disconnected. Reason: %v", - w.exchangeName, err) - } - err = w.Connect() - if err != nil { - log.Error(log.WebsocketMgr, err) - } - } else { - // pass off non disconnect errors to datahandler to manage - w.DataHandler <- err - } - case <-timer.C: - if !w.IsConnecting() && !w.IsConnected() { - err := w.Connect() - if err != nil { - log.Error(log.WebsocketMgr, err) - } - } - if !timer.Stop() { - select { - case <-timer.C: - default: - } - } - timer.Reset(connectionMonitorDelay) - } - } -} - -// Shutdown attempts to shut down a websocket connection and associated routines -// by using a package defined shutdown function -func (w *Websocket) Shutdown() error { - w.m.Lock() - defer func() { - w.Orderbook.FlushCache() - w.m.Unlock() - }() - if !w.IsConnected() { - return fmt.Errorf("%v cannot shutdown a disconnected websocket", w.exchangeName) - } - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v shutting down websocket channels", w.exchangeName) - } - close(w.ShutdownC) - w.Wg.Wait() - w.setConnectedStatus(false) - w.setConnectingStatus(false) - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v completed websocket channel shutdown", w.exchangeName) - } - return nil -} - -// trafficMonitor uses a timer of WebsocketTrafficLimitTime and once it expires -// Will reconnect if the TrafficAlert channel has not received any data -// The trafficTimer will reset on each traffic alert -func (w *Websocket) trafficMonitor(wg *sync.WaitGroup) { - w.Wg.Add(1) - wg.Done() - trafficTimer := time.NewTimer(w.trafficTimeout) - defer func() { - if !trafficTimer.Stop() { - select { - case <-trafficTimer.C: - default: - } - } - w.setTrafficMonitorRunning(false) - w.Wg.Done() - }() - if w.IsTrafficMonitorRunning() { - return - } - w.setTrafficMonitorRunning(true) - for { - select { - case <-w.ShutdownC: - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v trafficMonitor shutdown message received", w.exchangeName) - } - return - case <-w.TrafficAlert: - if !trafficTimer.Stop() { - select { - case <-trafficTimer.C: - default: - } - } - trafficTimer.Reset(w.trafficTimeout) - case <-trafficTimer.C: // Falls through when timer runs out - if w.verbose { - log.Warnf(log.WebsocketMgr, "%v has not received a traffic alert in %v. Reconnecting", w.exchangeName, w.trafficTimeout) - } - go w.Shutdown() - } - } -} - -func (w *Websocket) setConnectedStatus(b bool) { - w.connectionMutex.Lock() - w.connected = b - w.connectionMutex.Unlock() -} - -// IsConnected returns status of connection -func (w *Websocket) IsConnected() bool { - w.connectionMutex.RLock() - isConnected := w.connected - w.connectionMutex.RUnlock() - return isConnected -} - -func (w *Websocket) setConnectingStatus(b bool) { - w.connectionMutex.Lock() - w.connecting = b - w.connectionMutex.Unlock() -} - -// IsConnecting returns status of connecting -func (w *Websocket) IsConnecting() bool { - w.connectionMutex.RLock() - isConnecting := w.connecting - w.connectionMutex.RUnlock() - return isConnecting -} - -func (w *Websocket) setEnabled(b bool) { - w.connectionMutex.Lock() - w.enabled = b - w.connectionMutex.Unlock() -} - -// IsEnabled returns status of enabled -func (w *Websocket) IsEnabled() bool { - w.connectionMutex.RLock() - isEnabled := w.enabled - w.connectionMutex.RUnlock() - return isEnabled -} - -func (w *Websocket) setInit(b bool) { - w.connectionMutex.Lock() - w.init = b - w.connectionMutex.Unlock() -} - -// IsInit returns status of init -func (w *Websocket) IsInit() bool { - w.connectionMutex.RLock() - isInit := w.init - w.connectionMutex.RUnlock() - return isInit -} - -func (w *Websocket) setTrafficMonitorRunning(b bool) { - w.connectionMutex.Lock() - w.trafficMonitorRunning = b - w.connectionMutex.Unlock() -} - -// IsTrafficMonitorRunning returns status of the traffic monitor -func (w *Websocket) IsTrafficMonitorRunning() bool { - w.connectionMutex.RLock() - trafficMonRunning := w.trafficMonitorRunning - w.connectionMutex.RUnlock() - return trafficMonRunning -} - -func (w *Websocket) setConnectionMonitorRunning(b bool) { - w.connectionMutex.Lock() - w.connectionMonitorRunning = b - w.connectionMutex.Unlock() -} - -// IsConnectionMonitorRunning returns status of connection monitor -func (w *Websocket) IsConnectionMonitorRunning() bool { - w.connectionMutex.RLock() - isConnMonRunning := w.connectionMonitorRunning - w.connectionMutex.RUnlock() - return isConnMonRunning -} - -// CanUseAuthenticatedWebsocketForWrapper Handles a common check to -// verify whether a wrapper can use an authenticated websocket endpoint -func (w *Websocket) CanUseAuthenticatedWebsocketForWrapper() bool { - if w.IsConnected() && w.CanUseAuthenticatedEndpoints() { - return true - } else if w.IsConnected() && !w.CanUseAuthenticatedEndpoints() { - log.Infof(log.WebsocketMgr, WebsocketNotAuthenticatedUsingRest, w.exchangeName) - } - return false -} - -// SetWebsocketURL sets websocket URL -func (w *Websocket) SetWebsocketURL(websocketURL string) { - if websocketURL == "" || websocketURL == config.WebsocketURLNonDefaultMessage { - w.runningURL = w.defaultURL - return - } - w.runningURL = websocketURL -} - -// GetWebsocketURL returns the running websocket URL -func (w *Websocket) GetWebsocketURL() string { - return w.runningURL -} - -// Initialise verifies status and connects -func (w *Websocket) Initialise() error { - if w.IsEnabled() { - if w.IsInit() { - return nil - } - return fmt.Errorf("%v Websocket already initialised", - w.exchangeName) - } - w.setEnabled(w.enabled) - return nil -} - -// SetProxyAddress sets websocket proxy address -func (w *Websocket) SetProxyAddress(proxyAddr string) error { - if w.proxyAddr == proxyAddr { - return fmt.Errorf("%v Cannot set proxy address to the same address '%v'", w.exchangeName, w.proxyAddr) - } - - w.proxyAddr = proxyAddr - if !w.IsInit() && w.IsEnabled() { - if w.IsConnected() { - err := w.Shutdown() - if err != nil { - return err - } - } - return w.Connect() - } - return nil -} - -// GetProxyAddress returns the current websocket proxy -func (w *Websocket) GetProxyAddress() string { - return w.proxyAddr -} - -// SetDefaultURL sets default websocket URL -func (w *Websocket) SetDefaultURL(defaultURL string) { - w.defaultURL = defaultURL -} - -// GetDefaultURL returns the default websocket URL -func (w *Websocket) GetDefaultURL() string { - return w.defaultURL -} - -// SetConnector sets connection function -func (w *Websocket) SetConnector(connector func() error) { - w.connector = connector -} - -// SetExchangeName sets exchange name -func (w *Websocket) SetExchangeName(exchName string) { - w.exchangeName = exchName -} - -// GetName returns exchange name -func (w *Websocket) GetName() string { - return w.exchangeName -} - -// SetChannelSubscriber sets the function to use the base subscribe func -func (w *Websocket) SetChannelSubscriber(subscriber func(channelToSubscribe WebsocketChannelSubscription) error) { - w.channelSubscriber = subscriber -} - -// SetChannelUnsubscriber sets the function to use the base unsubscribe func -func (w *Websocket) SetChannelUnsubscriber(unsubscriber func(channelToUnsubscribe WebsocketChannelSubscription) error) { - w.channelUnsubscriber = unsubscriber -} - -// ManageSubscriptions ensures the subscriptions specified continue to be subscribed to -func (w *Websocket) manageSubscriptions() { - if !w.features.Subscribe && !w.features.Unsubscribe { - w.DataHandler <- fmt.Errorf("%v does not support channel subscriptions, exiting ManageSubscriptions()", w.exchangeName) - return - } - defer func() { - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v ManageSubscriptions exiting", w.exchangeName) - } - w.Wg.Done() - }() - for { - select { - case <-w.ShutdownC: - w.subscriptionMutex.Lock() - w.subscribedChannels = []WebsocketChannelSubscription{} - w.subscriptionMutex.Unlock() - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v shutdown manageSubscriptions", w.exchangeName) - } - return - default: - time.Sleep(manageSubscriptionsDelay) - if !w.IsConnected() { - w.subscriptionMutex.Lock() - w.subscribedChannels = []WebsocketChannelSubscription{} - w.subscriptionMutex.Unlock() - - continue - } - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v checking subscriptions", w.exchangeName) - } - // Subscribe to channels Pending a subscription - if w.features.Subscribe { - err := w.appendSubscribedChannels() - if err != nil { - w.DataHandler <- err - } - } - if w.features.Unsubscribe { - err := w.unsubscribeToChannels() - if err != nil { - w.DataHandler <- err - } - } - } - } -} - -// appendSubscribedChannels compares channelsToSubscribe to subscribedChannels -// and subscribes to any channels not present in subscribedChannels -func (w *Websocket) appendSubscribedChannels() error { - w.subscriptionMutex.Lock() - defer w.subscriptionMutex.Unlock() - for i := range w.channelsToSubscribe { - channelIsSubscribed := false - for j := 0; j < len(w.subscribedChannels); j++ { - if w.subscribedChannels[j].Equal(&w.channelsToSubscribe[i]) { - channelIsSubscribed = true - break - } - } - if !channelIsSubscribed { - if w.verbose { - log.Debugf(log.WebsocketMgr, "%v Subscribing to %v %v", w.exchangeName, w.channelsToSubscribe[i].Channel, w.channelsToSubscribe[i].Currency.String()) - } - err := w.channelSubscriber(w.channelsToSubscribe[i]) - if err != nil { - return err - } - w.subscribedChannels = append(w.subscribedChannels, w.channelsToSubscribe[i]) - } - } - return nil -} - -// unsubscribeToChannels compares subscribedChannels to channelsToSubscribe -// and unsubscribes to any channels not present in channelsToSubscribe -func (w *Websocket) unsubscribeToChannels() error { - w.subscriptionMutex.Lock() - defer w.subscriptionMutex.Unlock() - for i := range w.subscribedChannels { - subscriptionFound := false - for j := 0; j < len(w.channelsToSubscribe); j++ { - if w.channelsToSubscribe[j].Equal(&w.subscribedChannels[i]) { - subscriptionFound = true - break - } - } - if !subscriptionFound { - err := w.channelUnsubscriber(w.subscribedChannels[i]) - if err != nil { - return err - } - } - } - // Now that the slices should match, assign rather than looping and appending the differences - w.subscribedChannels = append(w.channelsToSubscribe[:0:0], w.channelsToSubscribe...) //nolint:gocritic - - return nil -} - -// RemoveSubscribedChannels removes supplied channels from channelsToSubscribe -func (w *Websocket) RemoveSubscribedChannels(channels []WebsocketChannelSubscription) { - for i := range channels { - w.removeChannelToSubscribe(channels[i]) - } -} - -// removeChannelToSubscribe removes an entry from w.channelsToSubscribe -// so an unsubscribe event can be triggered -func (w *Websocket) removeChannelToSubscribe(subscribedChannel WebsocketChannelSubscription) { - w.subscriptionMutex.Lock() - defer w.subscriptionMutex.Unlock() - channelLength := len(w.channelsToSubscribe) - i := 0 - for j := 0; j < len(w.channelsToSubscribe); j++ { - if !w.channelsToSubscribe[j].Equal(&subscribedChannel) { - w.channelsToSubscribe[i] = w.channelsToSubscribe[j] - i++ - } - } - w.channelsToSubscribe = w.channelsToSubscribe[:i] - if channelLength == len(w.channelsToSubscribe) { - w.DataHandler <- fmt.Errorf("%v removeChannelToSubscribe() Channel %v Currency %v could not be removed because it was not found", - w.exchangeName, - subscribedChannel.Channel, - subscribedChannel.Currency) - } -} - -// ResubscribeToChannel calls unsubscribe func and -// removes it from subscribedChannels to trigger a subscribe event -func (w *Websocket) ResubscribeToChannel(subscribedChannel WebsocketChannelSubscription) { - w.subscriptionMutex.Lock() - defer w.subscriptionMutex.Unlock() - err := w.channelUnsubscriber(subscribedChannel) - if err != nil { - w.DataHandler <- err - } - // Remove the channel from the list of subscribed channels - // ManageSubscriptions will automatically resubscribe - i := 0 - for j := 0; j < len(w.subscribedChannels); j++ { - if !w.subscribedChannels[j].Equal(&subscribedChannel) { - w.subscribedChannels[i] = w.subscribedChannels[j] - i++ - } - } - w.subscribedChannels = w.subscribedChannels[:i] -} - -// SubscribeToChannels appends supplied channels to channelsToSubscribe -func (w *Websocket) SubscribeToChannels(channels []WebsocketChannelSubscription) { - for i := range channels { - channelFound := false - for j := range w.channelsToSubscribe { - if w.channelsToSubscribe[j].Equal(&channels[i]) { - channelFound = true - } - } - if !channelFound { - w.channelsToSubscribe = append(w.channelsToSubscribe, channels[i]) - } - } -} - -// Equal two WebsocketChannelSubscription to determine equality -func (w *WebsocketChannelSubscription) Equal(subscribedChannel *WebsocketChannelSubscription) bool { - return strings.EqualFold(w.Channel, subscribedChannel.Channel) && - strings.EqualFold(w.Currency.String(), subscribedChannel.Currency.String()) -} - -// GetSubscriptions returns a copied list of subscriptions -// subscriptions is a private member and cannot be manipulated -func (w *Websocket) GetSubscriptions() []WebsocketChannelSubscription { - return append(w.subscribedChannels[:0:0], w.subscribedChannels...) -} - -// SetCanUseAuthenticatedEndpoints sets canUseAuthenticatedEndpoints val in -// a thread safe manner -func (w *Websocket) SetCanUseAuthenticatedEndpoints(val bool) { - w.subscriptionMutex.Lock() - defer w.subscriptionMutex.Unlock() - w.canUseAuthenticatedEndpoints = val -} - -// CanUseAuthenticatedEndpoints gets canUseAuthenticatedEndpoints val in -// a thread safe manner -func (w *Websocket) CanUseAuthenticatedEndpoints() bool { - w.subscriptionMutex.Lock() - canUseAuthEndpoints := w.canUseAuthenticatedEndpoints - w.subscriptionMutex.Unlock() - return canUseAuthEndpoints -} - -// SetResponseIDAndData adds data to IDResponses with locks and a nil check -func (w *WebsocketConnection) SetResponseIDAndData(id int64, data []byte) { - w.Lock() - defer w.Unlock() - if w.IDResponses == nil { - w.IDResponses = make(map[int64][]byte) - } - w.IDResponses[id] = data -} - -// Dial sets proxy urls and then connects to the websocket -func (w *WebsocketConnection) Dial(dialer *websocket.Dialer, headers http.Header) error { - if w.ProxyURL != "" { - proxy, err := url.Parse(w.ProxyURL) - if err != nil { - return err - } - dialer.Proxy = http.ProxyURL(proxy) - } - var err error - var conStatus *http.Response - w.Connection, conStatus, err = dialer.Dial(w.URL, headers) - if conStatus != nil { - conStatus.Body.Close() - } - if err != nil { - if conStatus != nil { - return fmt.Errorf("%v %v %v Error: %v", w.URL, conStatus, conStatus.StatusCode, err) - } - return fmt.Errorf("%v Error: %v", w.URL, err) - } - if w.Verbose { - log.Infof(log.WebsocketMgr, "%v Websocket connected to %s", w.ExchangeName, w.URL) - } - w.setConnectedStatus(true) - return nil -} - -// SendJSONMessage sends a JSON encoded message over the connection -func (w *WebsocketConnection) SendJSONMessage(data interface{}) error { - w.Lock() - defer w.Unlock() - if !w.IsConnected() { - return fmt.Errorf("%v cannot send message to a disconnected websocket", w.ExchangeName) - } - if w.Verbose { - log.Debugf(log.WebsocketMgr, - "%v sending message to websocket %+v", w.ExchangeName, data) - } - if w.RateLimit > 0 { - time.Sleep(time.Duration(w.RateLimit) * time.Millisecond) - } - return w.Connection.WriteJSON(data) -} - -// SendRawMessage sends a message over the connection without JSON encoding it -func (w *WebsocketConnection) SendRawMessage(messageType int, message []byte) error { - w.Lock() - defer w.Unlock() - if !w.IsConnected() { - return fmt.Errorf("%v cannot send message to a disconnected websocket", w.ExchangeName) - } - if w.Verbose { - log.Debugf(log.WebsocketMgr, - "%v sending message to websocket %s", w.ExchangeName, message) - } - if w.RateLimit > 0 { - time.Sleep(time.Duration(w.RateLimit) * time.Millisecond) - } - return w.Connection.WriteMessage(messageType, message) -} - -// SetupPingHandler will automatically send ping or pong messages based on -// WebsocketPingHandler configuration -func (w *WebsocketConnection) SetupPingHandler(handler WebsocketPingHandler) { - if handler.UseGorillaHandler { - h := func(msg string) error { - err := w.Connection.WriteControl(handler.MessageType, []byte(msg), time.Now().Add(handler.Delay)) - if err == websocket.ErrCloseSent { - return nil - } else if e, ok := err.(net.Error); ok && e.Temporary() { - return nil - } - return err - } - w.Connection.SetPingHandler(h) - return - } - w.Wg.Add(1) - defer w.Wg.Done() - go func() { - ticker := time.NewTicker(handler.Delay) - for { - select { - case <-w.Shutdown: - ticker.Stop() - return - case <-ticker.C: - err := w.SendRawMessage(handler.MessageType, handler.Message) - if err != nil { - log.Errorf(log.WebsocketMgr, - "%v failed to send message to websocket %s", w.ExchangeName, handler.Message) - return - } - } - } - }() -} - -// SendMessageReturnResponse will send a WS message to the connection -// It will then run a goroutine to await a JSON response -// If there is no response it will return an error -func (w *WebsocketConnection) SendMessageReturnResponse(id int64, request interface{}) ([]byte, error) { - err := w.SendJSONMessage(request) - if err != nil { - return nil, err - } - w.SetResponseIDAndData(id, nil) - var wg sync.WaitGroup - wg.Add(1) - go w.WaitForResult(id, &wg) - defer func() { - delete(w.IDResponses, id) - }() - wg.Wait() - if _, ok := w.IDResponses[id]; !ok { - return nil, fmt.Errorf("timeout waiting for response with ID %v", id) - } - - return w.IDResponses[id], nil -} - -// IsIDWaitingForResponse will verify whether the websocket is awaiting -// a response with a correlating ID. If true, the datahandler won't process -// the data, and instead will be processed by the wrapper function -func (w *WebsocketConnection) IsIDWaitingForResponse(id int64) bool { - w.Lock() - defer w.Unlock() - for k := range w.IDResponses { - if k == id && w.IDResponses[k] == nil { - return true - } - } - return false -} - -// WaitForResult will keep checking w.IDResponses for a response ID -// If the timer expires, it will return without -func (w *WebsocketConnection) WaitForResult(id int64, wg *sync.WaitGroup) { - defer wg.Done() - timer := time.NewTimer(w.ResponseMaxLimit) - for { - select { - case <-timer.C: - return - default: - w.Lock() - for k := range w.IDResponses { - if k == id && w.IDResponses[k] != nil { - w.Unlock() - if !timer.Stop() { - select { - case <-timer.C: - default: - } - } - return - } - } - w.Unlock() - time.Sleep(w.ResponseCheckTimeout) - } - } -} - -func (w *WebsocketConnection) setConnectedStatus(b bool) { - w.connectionMutex.Lock() - w.connected = b - w.connectionMutex.Unlock() -} - -// IsConnected exposes websocket connection status -func (w *WebsocketConnection) IsConnected() bool { - w.connectionMutex.RLock() - isConnected := w.connected - w.connectionMutex.RUnlock() - return isConnected -} - -// ReadMessage reads messages, can handle text, gzip and binary -func (w *WebsocketConnection) ReadMessage() (WebsocketResponse, error) { - mType, resp, err := w.Connection.ReadMessage() - if err != nil { - if isDisconnectionError(err) { - w.setConnectedStatus(false) - } - return WebsocketResponse{}, err - } - var standardMessage []byte - switch mType { - case websocket.TextMessage: - standardMessage = resp - case websocket.BinaryMessage: - standardMessage, err = w.parseBinaryResponse(resp) - if err != nil { - return WebsocketResponse{}, err - } - } - if w.Verbose { - log.Debugf(log.WebsocketMgr, "%v Websocket message received: %v", - w.ExchangeName, - string(standardMessage)) - } - return WebsocketResponse{Raw: standardMessage, Type: mType}, nil -} - -// parseBinaryResponse parses a websocket binary response into a usable byte array -func (w *WebsocketConnection) parseBinaryResponse(resp []byte) ([]byte, error) { - var standardMessage []byte - var err error - // Detect GZIP - if resp[0] == 31 && resp[1] == 139 { - b := bytes.NewReader(resp) - var gReader *gzip.Reader - gReader, err = gzip.NewReader(b) - if err != nil { - return standardMessage, err - } - standardMessage, err = ioutil.ReadAll(gReader) - if err != nil { - return standardMessage, err - } - err = gReader.Close() - if err != nil { - return standardMessage, err - } - } else { - reader := flate.NewReader(bytes.NewReader(resp)) - standardMessage, err = ioutil.ReadAll(reader) - if err != nil { - return standardMessage, err - } - err = reader.Close() - if err != nil { - return standardMessage, err - } - } - return standardMessage, nil -} - -// GenerateMessageID Creates a messageID to checkout -func (w *WebsocketConnection) GenerateMessageID(useNano bool) int64 { - if useNano { - return time.Now().UnixNano() - } - return time.Now().Unix() -} - -// isDisconnectionError Determines if the error sent over chan ReadMessageErrors is a disconnection error -func isDisconnectionError(err error) bool { - if websocket.IsUnexpectedCloseError(err) { - return true - } - switch err.(type) { - case *websocket.CloseError, *net.OpError: - return true - } - return false -} diff --git a/exchanges/websocket/wshandler/wshandler_test.go b/exchanges/websocket/wshandler/wshandler_test.go deleted file mode 100644 index d59c55e8..00000000 --- a/exchanges/websocket/wshandler/wshandler_test.go +++ /dev/null @@ -1,771 +0,0 @@ -package wshandler - -import ( - "bytes" - "compress/flate" - "compress/gzip" - "encoding/json" - "errors" - "net" - "net/http" - "os" - "strings" - "sync" - "testing" - "time" - - "github.com/gorilla/websocket" - "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" -) - -func TestTrafficMonitorTimeout(t *testing.T) { - ws := New() - err := ws.Setup( - &WebsocketSetup{ - Enabled: true, - AuthenticatedWebsocketAPISupport: true, - WebsocketTimeout: 10000, - DefaultURL: "testDefaultURL", - ExchangeName: "exchangeName", - RunningURL: "testRunningURL", - Connector: func() error { return nil }, - Subscriber: func(test WebsocketChannelSubscription) error { return nil }, - UnSubscriber: func(test WebsocketChannelSubscription) error { return nil }, - }) - if err != nil { - t.Error(err) - } - ws.setConnectedStatus(true) - ws.TrafficAlert = make(chan struct{}, 2) - ws.ShutdownC = make(chan struct{}) - var anotherWG sync.WaitGroup - anotherWG.Add(1) - go ws.trafficMonitor(&anotherWG) - anotherWG.Wait() - ws.TrafficAlert <- struct{}{} - trafficTimer := time.NewTimer(5 * time.Second) - select { - case <-trafficTimer.C: - t.Error("should be exiting") - default: - ws.Wg.Wait() - } -} - -func TestIsDisconnectionError(t *testing.T) { - isADisconnectionError := isDisconnectionError(errors.New("errorText")) - if isADisconnectionError { - t.Error("Its not") - } - isADisconnectionError = isDisconnectionError(&websocket.CloseError{ - Code: 1006, - Text: "errorText", - }) - if !isADisconnectionError { - t.Error("It is") - } - - isADisconnectionError = isDisconnectionError(&net.OpError{ - Op: "", - Net: "", - Source: nil, - Addr: nil, - Err: errors.New("errorText"), - }) - if !isADisconnectionError { - t.Error("It is") - } -} - -func TestConnectionMessageErrors(t *testing.T) { - ws := New() - ws.connected = true - ws.enabled = true - ws.ReadMessageErrors = make(chan error) - ws.DataHandler = make(chan interface{}) - ws.ShutdownC = make(chan struct{}) - ws.connector = func() error { return nil } - ws.features = &protocol.Features{} - go ws.connectionMonitor() - timer := time.NewTimer(900 * time.Millisecond) - ws.ReadMessageErrors <- errors.New("errorText") - select { - case err := <-ws.DataHandler: - if err.(error).Error() != "errorText" { - t.Errorf("Expected 'errorText', received %v", err) - } - case <-timer.C: - t.Error("Timeout waiting for datahandler to receive error") - } - timer = time.NewTimer(900 * time.Millisecond) - ws.ReadMessageErrors <- &websocket.CloseError{ - Code: 1006, - Text: "errorText", - } -outer: - for { - select { - case <-ws.DataHandler: - t.Fatal("Error is a disconnection error") - case <-timer.C: - break outer - } - } -} - -func TestWebsocket(t *testing.T) { - ws := Websocket{} - ws.setInit(true) - err := ws.Setup(&WebsocketSetup{ - ExchangeName: "test", - Enabled: true, - }) - if err != nil && err.Error() != "test Websocket already initialised" { - t.Errorf("Expected 'test Websocket already initialised', received %v", err) - } - - ws = *New() - err = ws.SetProxyAddress("testProxy") - if err != nil { - t.Error("SetProxyAddress", err) - } - - err = ws.Setup( - &WebsocketSetup{ - Enabled: true, - AuthenticatedWebsocketAPISupport: true, - WebsocketTimeout: 2, - DefaultURL: "testDefaultURL", - ExchangeName: "exchangeName", - RunningURL: "testRunningURL", - Connector: func() error { return nil }, - Subscriber: func(test WebsocketChannelSubscription) error { return nil }, - UnSubscriber: func(test WebsocketChannelSubscription) error { return nil }, - Features: &protocol.Features{}, - }) - if err != nil { - t.Error(err) - } - - if ws.GetName() != "exchangeName" { - t.Error("WebsocketSetup") - } - - if !ws.IsEnabled() { - t.Error("WebsocketSetup") - } - - ws.setEnabled(false) - if ws.IsEnabled() { - t.Error("WebsocketSetup") - } - - ws.setEnabled(true) - if !ws.IsEnabled() { - t.Error("WebsocketSetup") - } - - if ws.GetProxyAddress() != "testProxy" { - t.Error("WebsocketSetup") - } - - if ws.GetDefaultURL() != "testDefaultURL" { - t.Error("WebsocketSetup") - } - - if ws.GetWebsocketURL() != "testRunningURL" { - t.Error("WebsocketSetup") - } - - if ws.trafficTimeout != time.Duration(2) { - t.Error("WebsocketSetup") - } - // -- Not connected shutdown - err = ws.Shutdown() - if err == nil { - t.Fatal("should not be connected to able to shut down") - } - ws.Wg.Wait() - // -- Normal connect - err = ws.Connect() - if err != nil { - t.Fatal("WebsocketSetup", err) - } - ws.SetWebsocketURL("ws://demos.kaazing.com/echo") - // -- Already connected connect - err = ws.Connect() - if err == nil { - t.Fatal("should not connect, already connected") - } - // -- Normal shutdown - err = ws.Shutdown() - if err != nil { - t.Fatal("WebsocketSetup", err) - } - ws.Wg.Wait() -} - -// placeholderSubscriber basic function to test subscriptions -func placeholderSubscriber(channelToSubscribe WebsocketChannelSubscription) error { - return nil -} - -// TestSubscribe logic test -func TestSubscribe(t *testing.T) { - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{ - { - Channel: "hello", - }, - }, - subscribedChannels: []WebsocketChannelSubscription{}, - } - w.SetChannelSubscriber(placeholderSubscriber) - err := w.appendSubscribedChannels() - if err != nil { - t.Error(err) - } - if len(w.subscribedChannels) != 1 { - t.Errorf("Subscription did not occur") - } -} - -// TestSubscribe logic test -func TestSubscribeToChannels(t *testing.T) { - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{ - { - Channel: "hello", - }, - }, - subscribedChannels: []WebsocketChannelSubscription{}, - } - w.SetChannelSubscriber(placeholderSubscriber) - w.SubscribeToChannels([]WebsocketChannelSubscription{{Channel: "hello"}, {Channel: "hello2"}}) - if len(w.channelsToSubscribe) != 2 { - t.Errorf("Subscription did not occur") - } -} - -// TestUnsubscribe logic test -func TestUnsubscribe(t *testing.T) { - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{}, - subscribedChannels: []WebsocketChannelSubscription{ - { - Channel: "hello", - }, - }, - } - w.SetChannelUnsubscriber(placeholderSubscriber) - err := w.unsubscribeToChannels() - if err != nil { - t.Error(err) - } - if len(w.subscribedChannels) != 0 { - t.Errorf("Unsubscription did not occur") - } -} - -// TestSubscriptionWithExistingEntry logic test -func TestSubscriptionWithExistingEntry(t *testing.T) { - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{ - { - Channel: "hello", - }, - }, - subscribedChannels: []WebsocketChannelSubscription{ - { - Channel: "hello", - }, - }, - } - w.SetChannelSubscriber(placeholderSubscriber) - err := w.appendSubscribedChannels() - if err != nil { - t.Error(err) - } - if len(w.subscribedChannels) != 1 { - t.Errorf("Subscription should not have occurred") - } -} - -// TestUnsubscriptionWithExistingEntry logic test -func TestUnsubscriptionWithExistingEntry(t *testing.T) { - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{ - { - Channel: "hello", - }, - }, - subscribedChannels: []WebsocketChannelSubscription{ - { - Channel: "hello", - }, - }, - } - w.SetChannelUnsubscriber(placeholderSubscriber) - err := w.unsubscribeToChannels() - if err != nil { - t.Error(err) - } - if len(w.subscribedChannels) != 1 { - t.Errorf("Unsubscription should not have occurred") - } -} - -// TestManageSubscriptionsStartStop logic test -func TestManageSubscriptionsStartStop(t *testing.T) { - w := Websocket{ - ShutdownC: make(chan struct{}), - features: &protocol.Features{Subscribe: true, Unsubscribe: true}, - } - w.Wg.Add(1) - go w.manageSubscriptions() - close(w.ShutdownC) - w.Wg.Wait() -} - -// TestManageSubscriptions logic test -func TestManageSubscriptions(t *testing.T) { - w := Websocket{ - ShutdownC: make(chan struct{}), - features: &protocol.Features{Subscribe: true, Unsubscribe: true}, - subscribedChannels: []WebsocketChannelSubscription{ - { - Channel: "hello", - }, - }, - } - w.SetChannelUnsubscriber(placeholderSubscriber) - w.SetChannelSubscriber(placeholderSubscriber) - w.setConnectedStatus(true) - go w.manageSubscriptions() - time.Sleep(8 * time.Second) - w.setConnectedStatus(false) - time.Sleep(manageSubscriptionsDelay) - w.subscriptionMutex.Lock() - if len(w.subscribedChannels) > 0 { - t.Error("Expected empty subscribed channels") - } - w.subscriptionMutex.Unlock() -} - -// TestConnectionMonitorNoConnection logic test -func TestConnectionMonitorNoConnection(t *testing.T) { - ws := New() - ws.DataHandler = make(chan interface{}, 1) - ws.ShutdownC = make(chan struct{}, 1) - ws.exchangeName = "hello" - ws.trafficTimeout = 1 - go ws.connectionMonitor() - if ws.IsConnectionMonitorRunning() { - t.Fatal("Should have exited") - } -} - -// TestRemoveChannelToSubscribe logic test -func TestRemoveChannelToSubscribe(t *testing.T) { - subscription := WebsocketChannelSubscription{ - Channel: "hello", - } - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{ - subscription, - }, - } - w.SetChannelUnsubscriber(placeholderSubscriber) - w.removeChannelToSubscribe(subscription) - if len(w.subscribedChannels) != 0 { - t.Errorf("Unsubscription did not occur") - } -} - -// TestRemoveChannelToSubscribeWithNoSubscription logic test -func TestRemoveChannelToSubscribeWithNoSubscription(t *testing.T) { - subscription := WebsocketChannelSubscription{ - Channel: "hello", - } - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{}, - } - w.DataHandler = make(chan interface{}, 1) - w.SetChannelUnsubscriber(placeholderSubscriber) - go w.removeChannelToSubscribe(subscription) - err := <-w.DataHandler - if !strings.Contains(err.(error).Error(), "could not be removed because it was not found") { - t.Error("Expected not found error") - } -} - -// TestResubscribeToChannel logic test -func TestResubscribeToChannel(t *testing.T) { - subscription := WebsocketChannelSubscription{ - Channel: "hello", - } - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{}, - } - w.DataHandler = make(chan interface{}, 1) - w.SetChannelUnsubscriber(placeholderSubscriber) - w.SetChannelSubscriber(placeholderSubscriber) - w.ResubscribeToChannel(subscription) -} - -// TestSliceCopyDoesntImpactBoth logic test -func TestSliceCopyDoesntImpactBoth(t *testing.T) { - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{ - { - Channel: "hello1", - }, - { - Channel: "hello2", - }, - }, - subscribedChannels: []WebsocketChannelSubscription{ - { - Channel: "hello3", - }, - }, - } - w.SetChannelUnsubscriber(placeholderSubscriber) - err := w.unsubscribeToChannels() - if err != nil { - t.Error(err) - } - if len(w.subscribedChannels) != 2 { - t.Errorf("Unsubscription did not occur") - } - w.subscribedChannels[0].Channel = "test" - if strings.EqualFold(w.subscribedChannels[0].Channel, w.channelsToSubscribe[0].Channel) { - t.Errorf("Slice has not been copied appropriately") - } -} - -// TestSliceCopyDoesntImpactBoth logic test -func TestGetSubscriptions(t *testing.T) { - w := Websocket{ - subscribedChannels: []WebsocketChannelSubscription{ - { - Channel: "hello3", - }, - }, - } - - subs := w.GetSubscriptions() - subs[0].Channel = "noHELLO" - if strings.EqualFold(w.subscribedChannels[0].Channel, subs[0].Channel) { - t.Error("Subscriptions was not copied properly") - } -} - -// TestSetCanUseAuthenticatedEndpoints logic test -func TestSetCanUseAuthenticatedEndpoints(t *testing.T) { - ws := New() - result := ws.CanUseAuthenticatedEndpoints() - if result { - t.Error("expected `canUseAuthenticatedEndpoints` to be false") - } - ws.SetCanUseAuthenticatedEndpoints(true) - result = ws.CanUseAuthenticatedEndpoints() - if !result { - t.Error("expected `canUseAuthenticatedEndpoints` to be true") - } -} - -func TestRemoveSubscribedChannels(t *testing.T) { - w := Websocket{ - channelsToSubscribe: []WebsocketChannelSubscription{ - { - Channel: "hello3", - }, - }, - } - - w.RemoveSubscribedChannels([]WebsocketChannelSubscription{{Channel: "hello3"}}) - if len(w.channelsToSubscribe) == 1 { - t.Error("Did not remove subscription") - } -} - -const ( - websocketTestURL = "wss://www.bitmex.com/realtime" - returnResponseURL = "wss://ws.kraken.com" - useProxyTests = false // Disabled by default. Freely available proxy servers that work all the time are difficult to find - proxyURL = "http://212.186.171.4:80" // Replace with a usable proxy server -) - -var wc *WebsocketConnection -var dialer websocket.Dialer - -type testStruct struct { - Error error - WC WebsocketConnection -} - -type testRequest struct { - Event string `json:"event"` - RequestID int64 `json:"reqid,omitempty"` - Pairs []string `json:"pair"` - Subscription testRequestData `json:"subscription,omitempty"` -} - -// testRequestData contains details on WS channel -type testRequestData struct { - Name string `json:"name,omitempty"` - Interval int64 `json:"interval,omitempty"` - Depth int64 `json:"depth,omitempty"` -} - -type testResponse struct { - RequestID int64 `json:"reqid,omitempty"` -} - -// TestMain setup test -func TestMain(m *testing.M) { - wc = &WebsocketConnection{ - ExchangeName: "test", - URL: returnResponseURL, - ResponseMaxLimit: 7000000000, - ResponseCheckTimeout: 30000000, - } - os.Exit(m.Run()) -} - -// TestDial logic test -func TestDial(t *testing.T) { - var testCases = []testStruct{ - {Error: nil, WC: WebsocketConnection{ExchangeName: "test1", Verbose: true, URL: websocketTestURL, RateLimit: 10, ResponseCheckTimeout: 30000000, ResponseMaxLimit: 7000000000}}, - {Error: errors.New(" Error: malformed ws or wss URL"), WC: WebsocketConnection{ExchangeName: "test2", Verbose: true, URL: "", ResponseCheckTimeout: 30000000, ResponseMaxLimit: 7000000000}}, - {Error: nil, WC: WebsocketConnection{ExchangeName: "test3", Verbose: true, URL: websocketTestURL, ProxyURL: proxyURL, ResponseCheckTimeout: 30000000, ResponseMaxLimit: 7000000000}}, - } - for i := range testCases { - testData := &testCases[i] - t.Run(testData.WC.ExchangeName, func(t *testing.T) { - if testData.WC.ProxyURL != "" && !useProxyTests { - t.Skip("Proxy testing not enabled, skipping") - } - err := testData.WC.Dial(&dialer, http.Header{}) - if err != nil { - if testData.Error != nil && err.Error() == testData.Error.Error() { - return - } - t.Fatal(err) - } - }) - } -} - -// TestSendMessage logic test -func TestSendMessage(t *testing.T) { - var testCases = []testStruct{ - {Error: nil, WC: WebsocketConnection{ExchangeName: "test1", Verbose: true, URL: websocketTestURL, RateLimit: 10, ResponseCheckTimeout: 30000000, ResponseMaxLimit: 7000000000}}, - {Error: errors.New(" Error: malformed ws or wss URL"), WC: WebsocketConnection{ExchangeName: "test2", Verbose: true, URL: "", ResponseCheckTimeout: 30000000, ResponseMaxLimit: 7000000000}}, - {Error: nil, WC: WebsocketConnection{ExchangeName: "test3", Verbose: true, URL: websocketTestURL, ProxyURL: proxyURL, ResponseCheckTimeout: 30000000, ResponseMaxLimit: 7000000000}}, - } - for i := range testCases { - testData := &testCases[i] - t.Run(testData.WC.ExchangeName, func(t *testing.T) { - if testData.WC.ProxyURL != "" && !useProxyTests { - t.Skip("Proxy testing not enabled, skipping") - } - err := testData.WC.Dial(&dialer, http.Header{}) - if err != nil { - if testData.Error != nil && err.Error() == testData.Error.Error() { - return - } - t.Fatal(err) - } - err = testData.WC.SendJSONMessage(Ping) - if err != nil { - t.Error(err) - } - err = testData.WC.SendRawMessage(websocket.TextMessage, []byte(Ping)) - if err != nil { - t.Error(err) - } - }) - } -} - -// TestSendMessageWithResponse logic test -func TestSendMessageWithResponse(t *testing.T) { - if wc.ProxyURL != "" && !useProxyTests { - t.Skip("Proxy testing not enabled, skipping") - } - err := wc.Dial(&dialer, http.Header{}) - if err != nil { - t.Fatal(err) - } - go readMessages(wc, t) - - request := testRequest{ - Event: "subscribe", - Pairs: []string{currency.NewPairWithDelimiter("XBT", "USD", "/").String()}, - Subscription: testRequestData{ - Name: "ticker", - }, - RequestID: wc.GenerateMessageID(false), - } - _, err = wc.SendMessageReturnResponse(request.RequestID, request) - if err != nil { - t.Error(err) - } -} - -// TestSetupPingHandler logic test -func TestSetupPingHandler(t *testing.T) { - if wc.ProxyURL != "" && !useProxyTests { - t.Skip("Proxy testing not enabled, skipping") - } - wc.Shutdown = make(chan struct{}) - err := wc.Dial(&dialer, http.Header{}) - if err != nil { - t.Fatal(err) - } - - wc.SetupPingHandler(WebsocketPingHandler{ - UseGorillaHandler: true, - MessageType: websocket.PingMessage, - Delay: 1000, - }) - - err = wc.Connection.Close() - if err != nil { - t.Error(err) - } - - err = wc.Dial(&dialer, http.Header{}) - if err != nil { - t.Fatal(err) - } - wc.SetupPingHandler(WebsocketPingHandler{ - MessageType: websocket.TextMessage, - Message: []byte(Ping), - Delay: 200, - }) - time.Sleep(time.Millisecond * 500) - close(wc.Shutdown) - wc.Wg.Wait() -} - -// TestParseBinaryResponse logic test -func TestParseBinaryResponse(t *testing.T) { - var b bytes.Buffer - w := gzip.NewWriter(&b) - _, err := w.Write([]byte("hello")) - if err != nil { - t.Error(err) - } - err = w.Close() - if err != nil { - t.Error(err) - } - var resp []byte - resp, err = wc.parseBinaryResponse(b.Bytes()) - if err != nil { - t.Error(err) - } - if !strings.EqualFold(string(resp), "hello") { - t.Errorf("GZip conversion failed. Received: '%v', Expected: 'hello'", string(resp)) - } - - var b2 bytes.Buffer - w2, err2 := flate.NewWriter(&b2, 1) - if err2 != nil { - t.Error(err2) - } - _, err2 = w2.Write([]byte("hello")) - if err2 != nil { - t.Error(err) - } - err2 = w2.Close() - if err2 != nil { - t.Error(err) - } - resp2, err3 := wc.parseBinaryResponse(b2.Bytes()) - if err3 != nil { - t.Error(err3) - } - if !strings.EqualFold(string(resp2), "hello") { - t.Errorf("GZip conversion failed. Received: '%v', Expected: 'hello'", string(resp2)) - } -} - -// TestSetResponseIDAndData logic test -func TestSetResponseIDAndData(t *testing.T) { - wc.IDResponses = nil - wc.SetResponseIDAndData(0, nil) - wc.SetResponseIDAndData(1, []byte("hi")) - if len(wc.IDResponses) != 2 { - t.Error("Expected 2 entries") - } -} - -// TestIsIDWaitingForResponse logic test -func TestIsIDWaitingForResponse(t *testing.T) { - wc.IDResponses = nil - wc.SetResponseIDAndData(0, nil) - wc.SetResponseIDAndData(1, []byte("hi")) - if len(wc.IDResponses) != 2 { - t.Error("Expected 2 entries") - } - if !wc.IsIDWaitingForResponse(0) { - t.Error("Expected true") - } - if wc.IsIDWaitingForResponse(2) { - t.Error("Expected false") - } - if wc.IsIDWaitingForResponse(1337) { - t.Error("Expected false") - } -} - -// readMessages helper func -func readMessages(wc *WebsocketConnection, t *testing.T) { - timer := time.NewTimer(20 * time.Second) - for { - select { - case <-timer.C: - return - default: - resp, err := wc.ReadMessage() - if err != nil { - t.Error(err) - return - } - var incoming testResponse - err = json.Unmarshal(resp.Raw, &incoming) - if err != nil { - t.Error(err) - return - } - if incoming.RequestID > 0 { - wc.SetResponseIDAndData(incoming.RequestID, resp.Raw) - return - } - } - } -} - -// TestCanUseAuthenticatedWebsocketForWrapper logic test -func TestCanUseAuthenticatedWebsocketForWrapper(t *testing.T) { - ws := &Websocket{} - resp := ws.CanUseAuthenticatedWebsocketForWrapper() - if resp { - t.Error("Expected false, `connected` is false") - } - ws.setConnectedStatus(true) - resp = ws.CanUseAuthenticatedWebsocketForWrapper() - if resp { - t.Error("Expected false, `connected` is true and `CanUseAuthenticatedEndpoints` is false") - } - ws.canUseAuthenticatedEndpoints = true - resp = ws.CanUseAuthenticatedWebsocketForWrapper() - if !resp { - t.Error("Expected true, `connected` and `CanUseAuthenticatedEndpoints` is true") - } -} diff --git a/exchanges/websocket/wshandler/wshandler_types.go b/exchanges/websocket/wshandler/wshandler_types.go deleted file mode 100644 index b9180413..00000000 --- a/exchanges/websocket/wshandler/wshandler_types.go +++ /dev/null @@ -1,183 +0,0 @@ -package wshandler - -import ( - "sync" - "time" - - "github.com/gorilla/websocket" - "github.com/thrasher-corp/gocryptotrader/currency" - "github.com/thrasher-corp/gocryptotrader/exchanges/asset" - "github.com/thrasher-corp/gocryptotrader/exchanges/order" - "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook" -) - -// Websocket functionality list and state consts -const ( - // WebsocketNotEnabled alerts of a disabled websocket - WebsocketNotEnabled = "exchange_websocket_not_enabled" - manageSubscriptionsDelay = 5 * time.Second - // connection monitor time delays and limits - connectionMonitorDelay = 2 * time.Second - WebsocketNotAuthenticatedUsingRest = "%v - Websocket not authenticated, using REST" - Ping = "ping" - Pong = "pong" - UnhandledMessage = " - Unhandled websocket message: " -) - -// Websocket defines a return type for websocket connections via the interface -// wrapper for routine processing in routines.go -type Websocket struct { - canUseAuthenticatedEndpoints bool - enabled bool - init bool - connected bool - connecting bool - trafficMonitorRunning bool - verbose bool - connectionMonitorRunning bool - trafficTimeout time.Duration - proxyAddr string - defaultURL string - runningURL string - exchangeName string - m sync.Mutex - subscriptionMutex sync.Mutex - connectionMutex sync.RWMutex - connector func() error - subscribedChannels []WebsocketChannelSubscription - channelsToSubscribe []WebsocketChannelSubscription - channelSubscriber func(channelToSubscribe WebsocketChannelSubscription) error - channelUnsubscriber func(channelToUnsubscribe WebsocketChannelSubscription) error - DataHandler chan interface{} - // ShutdownC is the main shutdown channel which controls all websocket go funcs - ShutdownC chan struct{} - // Orderbook is a local cache of orderbooks - Orderbook wsorderbook.WebsocketOrderbookLocal - // Wg defines a wait group for websocket routines for cleanly shutting down - // routines - Wg sync.WaitGroup - // TrafficAlert monitors if there is a halt in traffic throughput - TrafficAlert chan struct{} - // ReadMessageErrors will received all errors from ws.ReadMessage() and verify if its a disconnection - ReadMessageErrors chan error - features *protocol.Features -} - -// WebsocketSetup defines variables for setting up a websocket connection -type WebsocketSetup struct { - Enabled bool - Verbose bool - AuthenticatedWebsocketAPISupport bool - WebsocketTimeout time.Duration - DefaultURL string - ExchangeName string - RunningURL string - Connector func() error - Subscriber func(channelToSubscribe WebsocketChannelSubscription) error - UnSubscriber func(channelToUnsubscribe WebsocketChannelSubscription) error - Features *protocol.Features -} - -// WebsocketChannelSubscription container for websocket subscriptions -// Currently only a one at a time thing to avoid complexity -type WebsocketChannelSubscription struct { - Channel string - Currency currency.Pair - Params map[string]interface{} -} - -// WebsocketResponse defines generalised data from the websocket connection -type WebsocketResponse struct { - Type int - Raw []byte -} - -// WebsocketOrderbookUpdate defines a websocket event in which the orderbook -// has been updated in the orderbook package -type WebsocketOrderbookUpdate struct { - Pair currency.Pair - Asset asset.Item - Exchange string -} - -// TradeData defines trade data -type TradeData struct { - Timestamp time.Time - CurrencyPair currency.Pair - AssetType asset.Item - Exchange string - EventType order.Type - Price float64 - Amount float64 - Side order.Side -} - -// FundingData defines funding data -type FundingData struct { - Timestamp time.Time - CurrencyPair currency.Pair - AssetType asset.Item - Exchange string - Amount float64 - Rate float64 - Period int64 - Side order.Side -} - -// KlineData defines kline feed -type KlineData struct { - Timestamp time.Time - Pair currency.Pair - AssetType asset.Item - Exchange string - StartTime time.Time - CloseTime time.Time - Interval string - OpenPrice float64 - ClosePrice float64 - HighPrice float64 - LowPrice float64 - Volume float64 -} - -// WebsocketPositionUpdated reflects a change in orders/contracts on an exchange -type WebsocketPositionUpdated struct { - Timestamp time.Time - Pair currency.Pair - AssetType asset.Item - Exchange string -} - -// WebsocketConnection contains all the data needed to send a message to a WS -type WebsocketConnection struct { - sync.Mutex - Verbose bool - connected bool - connectionMutex sync.RWMutex - RateLimit float64 - ExchangeName string - URL string - ProxyURL string - Wg sync.WaitGroup - Connection *websocket.Conn - Shutdown chan struct{} - // These are the request IDs and the corresponding response JSON - IDResponses map[int64][]byte - ResponseCheckTimeout time.Duration - ResponseMaxLimit time.Duration - TrafficTimeout time.Duration -} - -// WebsocketPingHandler container for ping handler settings -type WebsocketPingHandler struct { - UseGorillaHandler bool - MessageType int - Message []byte - Delay time.Duration -} - -// UnhandledMessageWarning is used for unhandled websocket messages -type UnhandledMessageWarning struct { - Message string -} diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 76a91a47..354644f3 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -20,7 +20,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,20 +55,11 @@ func (y *Yobit) SetDefaults() { y.API.CredentialsValidator.RequiresKey = true y.API.CredentialsValidator.RequiresSecret = true - y.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: false, - Separator: "-", - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Delimiter: currency.UnderscoreDelimiter, Separator: currency.DashDelimiter} + configFmt := ¤cy.PairFormat{Delimiter: currency.UnderscoreDelimiter, Uppercase: true} + err := y.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } y.Features = exchange.Features{ @@ -119,7 +109,6 @@ func (y *Yobit) Setup(exch *config.ExchangeConfig) error { y.SetEnabled(false) return nil } - return y.SetupDefaults(exch) } @@ -173,13 +162,19 @@ func (y *Yobit) UpdateTradablePairs(forceUpdate bool) error { if err != nil { return err } - - return y.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return y.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (y *Yobit) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - enabledPairs := y.GetEnabledPairs(assetType) + enabledPairs, err := y.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } pairsCollated, err := y.FormatExchangeCurrencies(enabledPairs, assetType) if err != nil { return nil, err @@ -191,24 +186,29 @@ func (y *Yobit) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Pri } for i := range enabledPairs { - curr := y.FormatExchangeCurrency(enabledPairs[i], assetType).Lower().String() + fpair, err := y.FormatExchangeCurrency(enabledPairs[i], assetType) + if err != nil { + return nil, err + } + curr := fpair.Lower().String() if _, ok := result[curr]; !ok { continue } - resultCurr := result[curr] - tickerPrice := new(ticker.Price) - tickerPrice.Pair = enabledPairs[i] - tickerPrice.Last = resultCurr.Last - tickerPrice.Ask = resultCurr.Sell - tickerPrice.Bid = resultCurr.Buy - tickerPrice.Last = resultCurr.Last - tickerPrice.Low = resultCurr.Low - tickerPrice.QuoteVolume = resultCurr.VolumeCurrent - tickerPrice.Volume = resultCurr.Vol - err = ticker.ProcessTicker(y.Name, tickerPrice, assetType) + resultCurr := result[curr] + err = ticker.ProcessTicker(&ticker.Price{ + Pair: enabledPairs[i], + Last: resultCurr.Last, + Ask: resultCurr.Sell, + Bid: resultCurr.Buy, + Low: resultCurr.Low, + QuoteVolume: resultCurr.VolumeCurrent, + Volume: resultCurr.Vol, + ExchangeName: y.Name, + AssetType: assetType, + }) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } return ticker.GetTicker(y.Name, p, assetType) @@ -235,7 +235,11 @@ func (y *Yobit) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderboo // UpdateOrderbook updates and returns the orderbook for a currency pair func (y *Yobit) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { orderBook := new(orderbook.Base) - orderbookNew, err := y.GetDepth(y.FormatExchangeCurrency(p, assetType).String()) + fpair, err := y.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } + orderbookNew, err := y.GetDepth(fpair.String()) if err != nil { return orderBook, err } @@ -376,10 +380,16 @@ func (y *Yobit) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error } var allActiveOrders []map[string]ActiveOrders - enabledPairs := y.GetEnabledPairs(asset.Spot) + enabledPairs, err := y.GetEnabledPairs(asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } for i := range enabledPairs { - fCurr := y.FormatExchangeCurrency(enabledPairs[i], asset.Spot).String() - activeOrdersForPair, err := y.GetOpenOrders(fCurr) + fCurr, err := y.FormatExchangeCurrency(enabledPairs[i], asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } + activeOrdersForPair, err := y.GetOpenOrders(fCurr.String()) if err != nil { return cancelAllOrdersResponse, err } @@ -446,11 +456,6 @@ func (y *Yobit) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.R return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (y *Yobit) GetWebsocket() (*wshandler.Websocket, error) { - return nil, common.ErrFunctionNotSupported -} - // GetFeeByType returns an estimate of fee based on type of transaction func (y *Yobit) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !y.AllowAuthenticatedRequest() && // Todo check connection status @@ -463,16 +468,28 @@ func (y *Yobit) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { // GetActiveOrders retrieves any orders that are active/open func (y *Yobit) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error) { var orders []order.Detail + + format, err := y.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + for x := range req.Pairs { - fCurr := y.FormatExchangeCurrency(req.Pairs[x], asset.Spot).String() - resp, err := y.GetOpenOrders(fCurr) + fCurr, err := y.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } + resp, err := y.GetOpenOrders(fCurr.String()) if err != nil { return nil, err } for id := range resp { - symbol := currency.NewPairDelimiter(resp[id].Pair, - y.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(resp[id].Pair, format.Delimiter) + if err != nil { + return nil, err + } orderDate := time.Unix(int64(resp[id].TimestampCreated), 0) side := order.Side(strings.ToUpper(resp[id].Type)) orders = append(orders, order.Detail{ @@ -497,13 +514,17 @@ func (y *Yobit) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, er func (y *Yobit) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error) { var allOrders []TradeHistory for x := range req.Pairs { + fpair, err := y.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } resp, err := y.GetTradeHistory(0, 10000, math.MaxInt64, req.StartTicks.Unix(), req.EndTicks.Unix(), "DESC", - y.FormatExchangeCurrency(req.Pairs[x], asset.Spot).String()) + fpair.String()) if err != nil { return nil, err } @@ -513,10 +534,18 @@ func (y *Yobit) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er } } + format, err := y.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range allOrders { - symbol := currency.NewPairDelimiter(allOrders[i].Pair, - y.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(allOrders[i].Pair, format.Delimiter) + if err != nil { + return nil, err + } orderDate := time.Unix(int64(allOrders[i].Timestamp), 0) side := order.Side(strings.ToUpper(allOrders[i].Type)) orders = append(orders, order.Detail{ @@ -535,28 +564,6 @@ func (y *Yobit) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, er return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (y *Yobit) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (y *Yobit) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (y *Yobit) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return nil, common.ErrFunctionNotSupported -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (y *Yobit) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (y *Yobit) ValidateCredentials() error { diff --git a/exchanges/zb/zb.go b/exchanges/zb/zb.go index e4a7080d..55cc7ede 100644 --- a/exchanges/zb/zb.go +++ b/exchanges/zb/zb.go @@ -16,7 +16,6 @@ import ( "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/request" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" ) const ( @@ -42,7 +41,6 @@ const ( // 47.91.169.147 api.zb.com // 47.52.55.212 trade.zb.com type ZB struct { - WebsocketConn *wshandler.WebsocketConnection exchange.Base } diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index d7ad8c46..e0667bc6 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -20,7 +20,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/kline" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -49,12 +49,11 @@ func TestMain(m *testing.M) { zbConfig.API.AuthenticatedWebsocketSupport = true zbConfig.API.Credentials.Key = apiKey zbConfig.API.Credentials.Secret = apiSecret + z.Websocket = sharedtestvalues.NewTestWebsocket() err = z.Setup(zbConfig) if err != nil { log.Fatal("ZB setup error", err) } - z.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride() - z.Websocket.TrafficAlert = sharedtestvalues.GetWebsocketStructChannelOverride() os.Exit(m.Run()) } @@ -63,22 +62,13 @@ func setupWsAuth(t *testing.T) { return } if !z.Websocket.IsEnabled() && !z.API.AuthenticatedWebsocketSupport || !z.ValidateAPICredentials() || !canManipulateRealOrders { - t.Skip(wshandler.WebsocketNotEnabled) - } - z.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: z.Name, - URL: zbWebsocketAPI, - Verbose: z.Verbose, - ResponseMaxLimit: exchange.DefaultWebsocketResponseMaxLimit, - ResponseCheckTimeout: exchange.DefaultWebsocketResponseCheckTimeout, + t.Skip(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := z.WebsocketConn.Dial(&dialer, http.Header{}) + err := z.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { t.Fatal(err) } - z.Websocket.DataHandler = make(chan interface{}, 11) - z.Websocket.TrafficAlert = make(chan struct{}, 11) go z.wsReadData() wsSetupRan = true } @@ -841,9 +831,12 @@ func TestWsCreateSubUserResponse(t *testing.T) { } func TestGetHistoricCandles(t *testing.T) { - currencyPair := currency.NewPairFromString("btc_usdt") + currencyPair, err := currency.NewPairFromString("btc_usdt") + if err != nil { + t.Fatal(err) + } startTime := time.Now().Add(-time.Hour * 1) - _, err := z.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) + _, err = z.GetHistoricCandles(currencyPair, asset.Spot, startTime, time.Now(), kline.OneHour) if err != nil { t.Fatal(err) } @@ -855,10 +848,13 @@ func TestGetHistoricCandles(t *testing.T) { } func TestGetHistoricCandlesExtended(t *testing.T) { - currencyPair := currency.NewPairFromString("btc_usdt") + currencyPair, err := currency.NewPairFromString("btc_usdt") + if err != nil { + t.Fatal(err) + } start := time.Now().AddDate(0, -2, 0) end := time.Now() - _, err := z.GetHistoricCandlesExtended(currencyPair, asset.Spot, start, end, kline.OneHour) + _, err = z.GetHistoricCandlesExtended(currencyPair, asset.Spot, start, end, kline.OneHour) if err != nil { t.Fatal(err) } diff --git a/exchanges/zb/zb_websocket.go b/exchanges/zb/zb_websocket.go index 8ad8fb45..bc453faf 100644 --- a/exchanges/zb/zb_websocket.go +++ b/exchanges/zb/zb_websocket.go @@ -11,14 +11,15 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" ) @@ -31,43 +32,36 @@ const ( // WsConnect initiates a websocket connection func (z *ZB) WsConnect() error { if !z.Websocket.IsEnabled() || !z.IsEnabled() { - return errors.New(wshandler.WebsocketNotEnabled) + return errors.New(stream.WebsocketNotEnabled) } var dialer websocket.Dialer - err := z.WebsocketConn.Dial(&dialer, http.Header{}) + err := z.Websocket.Conn.Dial(&dialer, http.Header{}) if err != nil { return err } + subs, err := z.GenerateDefaultSubscriptions() + if err != nil { + return err + } go z.wsReadData() - z.GenerateDefaultSubscriptions() - - return nil + return z.Websocket.SubscribeToChannels(subs) } // wsReadData handles all the websocket data coming from the websocket // connection func (z *ZB) wsReadData() { z.Websocket.Wg.Add(1) - defer func() { - z.Websocket.Wg.Done() - }() + defer z.Websocket.Wg.Done() for { - select { - case <-z.Websocket.ShutdownC: + resp := z.Websocket.Conn.ReadMessage() + if resp.Raw == nil { return - default: - resp, err := z.WebsocketConn.ReadMessage() - if err != nil { - z.Websocket.ReadMessageErrors <- err - return - } - z.Websocket.TrafficAlert <- struct{}{} - err = z.wsHandleData(resp.Raw) - if err != nil { - z.Websocket.DataHandler <- err - } + } + err := z.wsHandleData(resp.Raw) + if err != nil { + z.Websocket.DataHandler <- err } } } @@ -80,14 +74,17 @@ func (z *ZB) wsHandleData(respRaw []byte) error { return err } if result.No > 0 { - if z.WebsocketConn.IsIDWaitingForResponse(result.No) { - z.WebsocketConn.SetResponseIDAndData(result.No, fixedJSON) + if z.Websocket.Match.IncomingWithData(result.No, fixedJSON) { return nil } } if result.Code > 0 && result.Code != 1000 { - return fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, result.Message, wsErrCodes[result.Code]) + return fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + result.Message, + wsErrCodes[result.Code]) } + switch { case strings.Contains(result.Channel, "markets"): var markets Markets @@ -95,7 +92,6 @@ func (z *ZB) wsHandleData(respRaw []byte) error { if err != nil { return err } - case strings.Contains(result.Channel, "ticker"): cPair := strings.Split(result.Channel, "_") var wsTicker WsTicker @@ -104,6 +100,11 @@ func (z *ZB) wsHandleData(respRaw []byte) error { return err } + p, err := currency.NewPairFromString(cPair[0]) + if err != nil { + return err + } + z.Websocket.DataHandler <- &ticker.Price{ ExchangeName: z.Name, Close: wsTicker.Data.Last, @@ -115,9 +116,8 @@ func (z *ZB) wsHandleData(respRaw []byte) error { Ask: wsTicker.Data.Sell, LastUpdated: time.Unix(0, wsTicker.Date*int64(time.Millisecond)), AssetType: asset.Spot, - Pair: currency.NewPairFromString(cPair[0]), + Pair: p, } - case strings.Contains(result.Channel, "depth"): var depth WsDepth err := json.Unmarshal(fixedJSON, &depth) @@ -142,7 +142,11 @@ func (z *ZB) wsHandleData(respRaw []byte) error { } channelInfo := strings.Split(result.Channel, "_") - cPair := currency.NewPairFromString(channelInfo[0]) + cPair, err := currency.NewPairFromString(channelInfo[0]) + if err != nil { + return err + } + var newOrderBook orderbook.Base newOrderBook.Asks = asks newOrderBook.Bids = bids @@ -154,12 +158,6 @@ func (z *ZB) wsHandleData(respRaw []byte) error { if err != nil { return err } - - z.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{ - Pair: cPair, - Asset: asset.Spot, - Exchange: z.Name, - } case strings.Contains(result.Channel, "_order"): cPair := strings.Split(result.Channel, "_") var o WsSubmitOrderResponse @@ -168,9 +166,17 @@ func (z *ZB) wsHandleData(respRaw []byte) error { return err } if !o.Success { - return fmt.Errorf("%s - Order %v failed to be placed. %s", z.Name, o.Data.EntrustID, respRaw) + return fmt.Errorf("%s - Order %v failed to be placed. %s", + z.Name, + o.Data.EntrustID, + respRaw) } - p := currency.NewPairFromString(cPair[0]) + + p, err := currency.NewPairFromString(cPair[0]) + if err != nil { + return err + } + var a asset.Item a, err = z.GetPairAssetType(p) if err != nil { @@ -190,12 +196,21 @@ func (z *ZB) wsHandleData(respRaw []byte) error { return err } if !o.Success { - return fmt.Errorf("%s - Order %v failed to be cancelled. %s", z.Name, o.Data.EntrustID, respRaw) + return fmt.Errorf("%s - Order %v failed to be cancelled. %s", + z.Name, + o.Data.EntrustID, + respRaw) } + + p, err := currency.NewPairFromString(cPair[0]) + if err != nil { + return err + } + z.Websocket.DataHandler <- &order.Modify{ Exchange: z.Name, ID: strconv.FormatInt(o.Data.EntrustID, 10), - Pair: currency.NewPairFromString(cPair[0]), + Pair: p, Status: order.Cancelled, } case strings.Contains(result.Channel, "trades"): @@ -204,65 +219,86 @@ func (z *ZB) wsHandleData(respRaw []byte) error { if err != nil { return err } - // Most up to date trade - if len(trades.Data) == 0 { - return errors.New(z.Name + " - Empty websocket trade data received: " + string(fixedJSON)) - } - t := trades.Data[len(trades.Data)-1] - channelInfo := strings.Split(result.Channel, "_") - cPair := currency.NewPairFromString(channelInfo[0]) - tSide, err := order.StringToOrderSide(t.TradeType) - if err != nil { - z.Websocket.DataHandler <- order.ClassificationError{ - Exchange: z.Name, - Err: err, + for i := range trades.Data { + channelInfo := strings.Split(result.Channel, "_") + cPair, err := currency.NewPairFromString(channelInfo[0]) + if err != nil { + return err + } + + tSide, err := order.StringToOrderSide(trades.Data[i].TradeType) + if err != nil { + z.Websocket.DataHandler <- order.ClassificationError{ + Exchange: z.Name, + Err: err, + } + } + + z.Websocket.DataHandler <- stream.TradeData{ + Timestamp: time.Unix(trades.Data[i].Date, 0), + CurrencyPair: cPair, + AssetType: asset.Spot, + Exchange: z.Name, + Price: trades.Data[i].Price, + Amount: trades.Data[i].Amount, + Side: tSide, } } - z.Websocket.DataHandler <- wshandler.TradeData{ - Timestamp: time.Unix(t.Date, 0), - CurrencyPair: cPair, - AssetType: asset.Spot, - Exchange: z.Name, - Price: t.Price, - Amount: t.Amount, - Side: tSide, - } default: - z.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: z.Name + wshandler.UnhandledMessage + string(respRaw)} - return nil + z.Websocket.DataHandler <- stream.UnhandledMessageWarning{ + Message: z.Name + + stream.UnhandledMessage + + string(respRaw)} } return nil } // GenerateDefaultSubscriptions Adds default subscriptions to websocket to be handled by ManageSubscriptions() -func (z *ZB) GenerateDefaultSubscriptions() { - var subscriptions []wshandler.WebsocketChannelSubscription - // Tickerdata is its own channel - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ +func (z *ZB) GenerateDefaultSubscriptions() ([]stream.ChannelSubscription, error) { + var subscriptions []stream.ChannelSubscription + // market configuration is its own channel + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: "markets", }) channels := []string{"%s_ticker", "%s_depth", "%s_trades"} - enabledCurrencies := z.GetEnabledPairs(asset.Spot) + enabledCurrencies, err := z.GetEnabledPairs(asset.Spot) + if err != nil { + return nil, err + } + for i := range channels { for j := range enabledCurrencies { enabledCurrencies[j].Delimiter = "" - subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{ + subscriptions = append(subscriptions, stream.ChannelSubscription{ Channel: fmt.Sprintf(channels[i], enabledCurrencies[j].Lower().String()), Currency: enabledCurrencies[j].Lower(), + Asset: asset.Spot, }) } } - z.Websocket.SubscribeToChannels(subscriptions) + return subscriptions, nil } // Subscribe sends a websocket message to receive data from the channel -func (z *ZB) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error { - subscriptionRequest := Subscription{ - Event: zWebsocketAddChannel, - Channel: channelToSubscribe.Channel, +func (z *ZB) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { + var errs common.Errors + for i := range channelsToSubscribe { + subscriptionRequest := Subscription{ + Event: zWebsocketAddChannel, + Channel: channelsToSubscribe[i].Channel, + } + err := z.Websocket.Conn.SendJSONMessage(subscriptionRequest) + if err != nil { + errs = append(errs, err) + continue + } + z.Websocket.AddSuccessfulSubscriptions(channelsToSubscribe[i]) } - return z.WebsocketConn.SendJSONMessage(subscriptionRequest) + if errs != nil { + return errs + } + return nil } func (z *ZB) wsGenerateSignature(request interface{}) string { @@ -303,9 +339,9 @@ func (z *ZB) wsAddSubUser(username, password string) (*WsGetSubUserListResponse, request.Channel = "addSubUser" request.Event = zWebsocketAddChannel request.Accesskey = z.API.Credentials.Key - request.No = z.WebsocketConn.GenerateMessageID(true) + request.No = z.Websocket.Conn.GenerateMessageID(true) request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -315,7 +351,11 @@ func (z *ZB) wsAddSubUser(username, password string) (*WsGetSubUserListResponse, return nil, err } if genericResponse.Code > 0 && genericResponse.Code != 1000 { - return nil, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, genericResponse.Message, wsErrCodes[genericResponse.Code]) + return nil, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + genericResponse.Message, + wsErrCodes[genericResponse.Code]) } var response WsGetSubUserListResponse err = json.Unmarshal(resp, &response) @@ -327,16 +367,17 @@ func (z *ZB) wsAddSubUser(username, password string) (*WsGetSubUserListResponse, func (z *ZB) wsGetSubUserList() (*WsGetSubUserListResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsAuthenticatedRequest{} request.Channel = "getSubUserList" request.Event = zWebsocketAddChannel request.Accesskey = z.API.Credentials.Key - request.No = z.WebsocketConn.GenerateMessageID(true) + request.No = z.Websocket.Conn.GenerateMessageID(true) request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -346,28 +387,33 @@ func (z *ZB) wsGetSubUserList() (*WsGetSubUserListResponse, error) { return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } func (z *ZB) wsDoTransferFunds(pair currency.Code, amount float64, fromUserName, toUserName string) (*WsRequestResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsDoTransferFundsRequest{ Amount: amount, Currency: pair, FromUserName: fromUserName, ToUserName: toUserName, - No: z.WebsocketConn.GenerateMessageID(true), + No: z.Websocket.Conn.GenerateMessageID(true), } request.Channel = "doTransferFunds" request.Event = zWebsocketAddChannel request.Accesskey = z.API.Credentials.Key request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -377,14 +423,19 @@ func (z *ZB) wsDoTransferFunds(pair currency.Code, amount float64, fromUserName, return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } func (z *ZB) wsCreateSubUserKey(assetPerm, entrustPerm, leverPerm, moneyPerm bool, keyName, toUserID string) (*WsRequestResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsCreateSubUserKeyRequest{ AssetPerm: assetPerm, @@ -392,7 +443,7 @@ func (z *ZB) wsCreateSubUserKey(assetPerm, entrustPerm, leverPerm, moneyPerm boo KeyName: keyName, LeverPerm: leverPerm, MoneyPerm: moneyPerm, - No: z.WebsocketConn.GenerateMessageID(true), + No: z.Websocket.Conn.GenerateMessageID(true), ToUserID: toUserID, } request.Channel = "createSubUserKey" @@ -400,7 +451,7 @@ func (z *ZB) wsCreateSubUserKey(assetPerm, entrustPerm, leverPerm, moneyPerm boo request.Accesskey = z.API.Credentials.Key request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -410,27 +461,32 @@ func (z *ZB) wsCreateSubUserKey(assetPerm, entrustPerm, leverPerm, moneyPerm boo return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } func (z *ZB) wsSubmitOrder(pair currency.Pair, amount, price float64, tradeType int64) (*WsSubmitOrderResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsSubmitOrderRequest{ Amount: amount, Price: price, TradeType: tradeType, - No: z.WebsocketConn.GenerateMessageID(true), + No: z.Websocket.Conn.GenerateMessageID(true), } request.Channel = pair.String() + "_order" request.Event = zWebsocketAddChannel request.Accesskey = z.API.Credentials.Key request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -440,25 +496,30 @@ func (z *ZB) wsSubmitOrder(pair currency.Pair, amount, price float64, tradeType return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } func (z *ZB) wsCancelOrder(pair currency.Pair, orderID int64) (*WsCancelOrderResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsCancelOrderRequest{ ID: orderID, - No: z.WebsocketConn.GenerateMessageID(true), + No: z.Websocket.Conn.GenerateMessageID(true), } request.Channel = pair.String() + "_cancelorder" request.Event = zWebsocketAddChannel request.Accesskey = z.API.Credentials.Key request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -468,25 +529,30 @@ func (z *ZB) wsCancelOrder(pair currency.Pair, orderID int64) (*WsCancelOrderRes return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } func (z *ZB) wsGetOrder(pair currency.Pair, orderID int64) (*WsGetOrderResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsGetOrderRequest{ ID: orderID, - No: z.WebsocketConn.GenerateMessageID(true), + No: z.Websocket.Conn.GenerateMessageID(true), } request.Channel = pair.String() + "_getorder" request.Event = zWebsocketAddChannel request.Accesskey = z.API.Credentials.Key request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -496,25 +562,30 @@ func (z *ZB) wsGetOrder(pair currency.Pair, orderID int64) (*WsGetOrderResponse, return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } func (z *ZB) wsGetOrders(pair currency.Pair, pageIndex, tradeType int64) (*WsGetOrdersResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsGetOrdersRequest{ PageIndex: pageIndex, TradeType: tradeType, - No: z.WebsocketConn.GenerateMessageID(true), + No: z.Websocket.Conn.GenerateMessageID(true), } request.Channel = pair.String() + "_getorders" request.Event = zWebsocketAddChannel request.Accesskey = z.API.Credentials.Key request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -524,26 +595,31 @@ func (z *ZB) wsGetOrders(pair currency.Pair, pageIndex, tradeType int64) (*WsGet return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } func (z *ZB) wsGetOrdersIgnoreTradeType(pair currency.Pair, pageIndex, pageSize int64) (*WsGetOrdersIgnoreTradeTypeResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsGetOrdersIgnoreTradeTypeRequest{ PageIndex: pageIndex, PageSize: pageSize, - No: z.WebsocketConn.GenerateMessageID(true), + No: z.Websocket.Conn.GenerateMessageID(true), } request.Channel = pair.String() + "_getordersignoretradetype" request.Event = zWebsocketAddChannel request.Accesskey = z.API.Credentials.Key request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -553,24 +629,29 @@ func (z *ZB) wsGetOrdersIgnoreTradeType(pair currency.Pair, pageIndex, pageSize return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } func (z *ZB) wsGetAccountInfoRequest() (*WsGetAccountInfoResponse, error) { if !z.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) { - return nil, fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) + return nil, + fmt.Errorf("%v AuthenticatedWebsocketAPISupport not enabled", z.Name) } request := WsAuthenticatedRequest{ Channel: "getaccountinfo", Event: zWebsocketAddChannel, Accesskey: z.API.Credentials.Key, - No: z.WebsocketConn.GenerateMessageID(true), + No: z.Websocket.Conn.GenerateMessageID(true), } request.Sign = z.wsGenerateSignature(request) - resp, err := z.WebsocketConn.SendMessageReturnResponse(request.No, request) + resp, err := z.Websocket.Conn.SendMessageReturnResponse(request.No, request) if err != nil { return nil, err } @@ -580,7 +661,11 @@ func (z *ZB) wsGetAccountInfoRequest() (*WsGetAccountInfoResponse, error) { return nil, err } if response.Code > 0 && response.Code != 1000 { - return &response, fmt.Errorf("%v request failed, message: %v, error code: %v", z.Name, response.Message, wsErrCodes[response.Code]) + return &response, + fmt.Errorf("%v request failed, message: %v, error code: %v", + z.Name, + response.Message, + wsErrCodes[response.Code]) } return &response, nil } diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index c5a003d6..f4778160 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -19,8 +19,8 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/protocol" "github.com/thrasher-corp/gocryptotrader/exchanges/request" + "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" - "github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler" "github.com/thrasher-corp/gocryptotrader/log" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -56,19 +56,11 @@ func (z *ZB) SetDefaults() { z.API.CredentialsValidator.RequiresKey = true z.API.CredentialsValidator.RequiresSecret = true - z.CurrencyPairs = currency.PairsManager{ - AssetTypes: asset.Items{ - asset.Spot, - }, - - UseGlobalFormat: true, - RequestFormat: ¤cy.PairFormat{ - Delimiter: "_", - }, - ConfigFormat: ¤cy.PairFormat{ - Delimiter: "_", - Uppercase: true, - }, + requestFmt := ¤cy.PairFormat{Delimiter: currency.UnderscoreDelimiter} + configFmt := ¤cy.PairFormat{Delimiter: currency.UnderscoreDelimiter, Uppercase: true} + err := z.SetGlobalPairsManager(requestFmt, configFmt, asset.Spot) + if err != nil { + log.Errorln(log.ExchangeSys, err) } z.Features = exchange.Features{ @@ -142,7 +134,7 @@ func (z *ZB) SetDefaults() { z.API.Endpoints.URLSecondaryDefault = zbMarketURL z.API.Endpoints.URLSecondary = z.API.Endpoints.URLSecondaryDefault z.API.Endpoints.WebsocketURL = zbWebsocketAPI - z.Websocket = wshandler.New() + z.Websocket = stream.New() z.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit z.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout } @@ -159,33 +151,30 @@ func (z *ZB) Setup(exch *config.ExchangeConfig) error { return err } - err = z.Websocket.Setup( - &wshandler.WebsocketSetup{ - Enabled: exch.Features.Enabled.Websocket, - Verbose: exch.Verbose, - AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, - WebsocketTimeout: exch.WebsocketTrafficTimeout, - DefaultURL: zbWebsocketAPI, - ExchangeName: exch.Name, - RunningURL: exch.API.Endpoints.WebsocketURL, - Connector: z.WsConnect, - Subscriber: z.Subscribe, - Features: &z.Features.Supports.WebsocketCapabilities, - }) + err = z.Websocket.Setup(&stream.WebsocketSetup{ + Enabled: exch.Features.Enabled.Websocket, + Verbose: exch.Verbose, + AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport, + WebsocketTimeout: exch.WebsocketTrafficTimeout, + DefaultURL: zbWebsocketAPI, + ExchangeName: exch.Name, + RunningURL: exch.API.Endpoints.WebsocketURL, + Connector: z.WsConnect, + GenerateSubscriptions: z.GenerateDefaultSubscriptions, + Subscriber: z.Subscribe, + Features: &z.Features.Supports.WebsocketCapabilities, + OrderbookBufferLimit: exch.WebsocketOrderbookBufferLimit, + }) if err != nil { return err } - z.WebsocketConn = &wshandler.WebsocketConnection{ - ExchangeName: z.Name, + return z.Websocket.SetupNewConnection(stream.ConnectionSetup{ URL: z.Websocket.GetWebsocketURL(), - ProxyURL: z.Websocket.GetProxyAddress(), - Verbose: z.Verbose, RateLimit: zbWebsocketRateLimit, ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout, ResponseMaxLimit: exch.WebsocketResponseMaxLimit, - } - return nil + }) } // Start starts the OKEX go routine @@ -235,19 +224,24 @@ func (z *ZB) UpdateTradablePairs(forceUpdate bool) error { if err != nil { return err } - return z.UpdatePairs(currency.NewPairsFromStrings(pairs), asset.Spot, false, forceUpdate) + p, err := currency.NewPairsFromStrings(pairs) + if err != nil { + return err + } + return z.UpdatePairs(p, asset.Spot, false, forceUpdate) } // UpdateTicker updates and returns the ticker for a currency pair func (z *ZB) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) { - tickerPrice := new(ticker.Price) - result, err := z.GetTickers() if err != nil { - return tickerPrice, err + return nil, err } - enabledPairs := z.GetEnabledPairs(assetType) + enabledPairs, err := z.GetEnabledPairs(assetType) + if err != nil { + return nil, err + } for x := range enabledPairs { // We can't use either pair format here, so format it to lower- // case and without any delimiter @@ -255,18 +249,19 @@ func (z *ZB) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, if _, ok := result[curr]; !ok { continue } - var tp ticker.Price - tp.Pair = enabledPairs[x] - tp.High = result[curr].High - tp.Last = result[curr].Last - tp.Ask = result[curr].Sell - tp.Bid = result[curr].Buy - tp.Low = result[curr].Low - tp.Volume = result[curr].Volume - err = ticker.ProcessTicker(z.Name, &tp, assetType) + err = ticker.ProcessTicker(&ticker.Price{ + Pair: enabledPairs[x], + High: result[curr].High, + Last: result[curr].Last, + Ask: result[curr].Sell, + Bid: result[curr].Buy, + Low: result[curr].Low, + Volume: result[curr].Volume, + ExchangeName: z.Name, + AssetType: assetType}) if err != nil { - log.Error(log.Ticker, err) + return nil, err } } @@ -294,9 +289,12 @@ func (z *ZB) FetchOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.B // UpdateOrderbook updates and returns the orderbook for a currency pair func (z *ZB) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) { orderBook := new(orderbook.Base) - curr := z.FormatExchangeCurrency(p, assetType).String() + currFormat, err := z.FormatExchangeCurrency(p, assetType) + if err != nil { + return nil, err + } - orderbookNew, err := z.GetOrderbook(curr) + orderbookNew, err := z.GetOrderbook(currFormat.String()) if err != nil { return orderBook, err } @@ -473,8 +471,11 @@ func (z *ZB) CancelOrder(o *order.Cancel) error { } return nil } - return z.CancelExistingOrder(orderIDInt, z.FormatExchangeCurrency(o.Pair, - o.AssetType).String()) + fpair, err := z.FormatExchangeCurrency(o.Pair, o.AssetType) + if err != nil { + return err + } + return z.CancelExistingOrder(orderIDInt, fpair.String()) } // CancelAllOrders cancels all orders associated with a currency pair @@ -483,11 +484,18 @@ func (z *ZB) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) { Status: make(map[string]string), } var allOpenOrders []Order - enabledPairs := z.GetEnabledPairs(asset.Spot) + enabledPairs, err := z.GetEnabledPairs(asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } + for x := range enabledPairs { - fPair := z.FormatExchangeCurrency(enabledPairs[x], asset.Spot).String() + fPair, err := z.FormatExchangeCurrency(enabledPairs[x], asset.Spot) + if err != nil { + return cancelAllOrdersResponse, err + } for y := int64(1); ; y++ { - openOrders, err := z.GetUnfinishedOrdersIgnoreTradeType(fPair, y, 10) + openOrders, err := z.GetUnfinishedOrdersIgnoreTradeType(fPair.String(), y, 10) if err != nil { if strings.Contains(err.Error(), "3001") { break @@ -508,9 +516,15 @@ func (z *ZB) CancelAllOrders(_ *order.Cancel) (order.CancelAllResponse, error) { } for i := range allOpenOrders { - err := z.CancelOrder(&order.Cancel{ + p, err := currency.NewPairFromString(allOpenOrders[i].Currency) + if err != nil { + cancelAllOrdersResponse.Status[strconv.FormatInt(allOpenOrders[i].ID, 10)] = err.Error() + continue + } + + err = z.CancelOrder(&order.Cancel{ ID: strconv.FormatInt(allOpenOrders[i].ID, 10), - Pair: currency.NewPairFromString(allOpenOrders[i].Currency), + Pair: p, }) if err != nil { cancelAllOrdersResponse.Status[strconv.FormatInt(allOpenOrders[i].ID, 10)] = err.Error() @@ -560,11 +574,6 @@ func (z *ZB) WithdrawFiatFundsToInternationalBank(withdrawRequest *withdraw.Requ return nil, common.ErrFunctionNotSupported } -// GetWebsocket returns a pointer to the exchange websocket -func (z *ZB) GetWebsocket() (*wshandler.Websocket, error) { - return z.Websocket, nil -} - // GetFeeByType returns an estimate of fee based on type of transaction func (z *ZB) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) { if !z.AllowAuthenticatedRequest() && // Todo check connection status @@ -580,8 +589,11 @@ func (z *ZB) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error var allOrders []Order for x := range req.Pairs { for i := int64(1); ; i++ { - fPair := z.FormatExchangeCurrency(req.Pairs[x], asset.Spot).String() - resp, err := z.GetUnfinishedOrdersIgnoreTradeType(fPair, i, 10) + fPair, err := z.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } + resp, err := z.GetUnfinishedOrdersIgnoreTradeType(fPair.String(), i, 10) if err != nil { if strings.Contains(err.Error(), "3001") { break @@ -601,10 +613,19 @@ func (z *ZB) GetActiveOrders(req *order.GetOrdersRequest) ([]order.Detail, error } } + format, err := z.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + var orders []order.Detail for i := range allOrders { - symbol := currency.NewPairDelimiter(allOrders[i].Currency, - z.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(allOrders[i].Currency, + format.Delimiter) + if err != nil { + return nil, err + } orderDate := time.Unix(int64(allOrders[i].TradeDate), 0) orderSide := orderSideMap[allOrders[i].Type] orders = append(orders, order.Detail{ @@ -653,8 +674,11 @@ func (z *ZB) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error } for x := range req.Pairs { for y := int64(1); ; y++ { - fPair := z.FormatExchangeCurrency(req.Pairs[x], asset.Spot).String() - resp, err := z.GetOrders(fPair, y, side) + fPair, err := z.FormatExchangeCurrency(req.Pairs[x], asset.Spot) + if err != nil { + return nil, err + } + resp, err := z.GetOrders(fPair.String(), y, side) if err != nil { return nil, err } @@ -669,9 +693,18 @@ func (z *ZB) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error } } + format, err := z.GetPairFormat(asset.Spot, false) + if err != nil { + return nil, err + } + for i := range allOrders { - symbol := currency.NewPairDelimiter(allOrders[i].Currency, - z.GetPairFormat(asset.Spot, false).Delimiter) + var symbol currency.Pair + symbol, err = currency.NewPairDelimiter(allOrders[i].Currency, + format.Delimiter) + if err != nil { + return nil, err + } orderDate := time.Unix(int64(allOrders[i].TradeDate), 0) orderSide := orderSideMap[allOrders[i].Type] orders = append(orders, order.Detail{ @@ -689,29 +722,6 @@ func (z *ZB) GetOrderHistory(req *order.GetOrdersRequest) ([]order.Detail, error return orders, nil } -// SubscribeToWebsocketChannels appends to ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle subscribing -func (z *ZB) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - z.Websocket.SubscribeToChannels(channels) - return nil -} - -// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe -// which lets websocket.manageSubscriptions handle unsubscribing -func (z *ZB) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error { - return common.ErrFunctionNotSupported -} - -// GetSubscriptions returns a copied list of subscriptions -func (z *ZB) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) { - return z.Websocket.GetSubscriptions(), nil -} - -// AuthenticateWebsocket sends an authentication message to the websocket -func (z *ZB) AuthenticateWebsocket() error { - return common.ErrFunctionNotSupported -} - // ValidateCredentials validates current credentials used for wrapper // functionality func (z *ZB) ValidateCredentials() error { @@ -745,9 +755,14 @@ func (z *ZB) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end tim } } + formattedPair, err := z.FormatExchangeCurrency(pair, a) + if err != nil { + return kline.Item{}, err + } + klineParams := KlinesRequestParams{ Type: z.FormatExchangeKlineInterval(interval), - Symbol: z.FormatExchangeCurrency(pair, a).String(), + Symbol: formattedPair.String(), } candles, err := z.GetSpotKline(klineParams) diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index 8a2594f1..d926f207 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -1,79 +1,63 @@ // Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.22.0-devel -// protoc v3.12.3 // source: rpc.proto package gctrpc import ( context "context" + fmt "fmt" proto "github.com/golang/protobuf/proto" timestamp "github.com/golang/protobuf/ptypes/timestamp" _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" + math "math" ) -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type GetInfoRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetInfoRequest) Reset() { - *x = GetInfoRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetInfoRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetInfoRequest) ProtoMessage() {} - -func (x *GetInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetInfoRequest.ProtoReflect.Descriptor instead. +func (m *GetInfoRequest) Reset() { *m = GetInfoRequest{} } +func (m *GetInfoRequest) String() string { return proto.CompactTextString(m) } +func (*GetInfoRequest) ProtoMessage() {} func (*GetInfoRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{0} + return fileDescriptor_77a6da22d6a3feb1, []int{0} } +func (m *GetInfoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetInfoRequest.Unmarshal(m, b) +} +func (m *GetInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetInfoRequest.Marshal(b, m, deterministic) +} +func (m *GetInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetInfoRequest.Merge(m, src) +} +func (m *GetInfoRequest) XXX_Size() int { + return xxx_messageInfo_GetInfoRequest.Size(m) +} +func (m *GetInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetInfoRequest proto.InternalMessageInfo + type GetInfoResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - Uptime string `protobuf:"bytes,1,opt,name=uptime,proto3" json:"uptime,omitempty"` AvailableExchanges int64 `protobuf:"varint,2,opt,name=available_exchanges,json=availableExchanges,proto3" json:"available_exchanges,omitempty"` EnabledExchanges int64 `protobuf:"varint,3,opt,name=enabled_exchanges,json=enabledExchanges,proto3" json:"enabled_exchanges,omitempty"` @@ -81,10860 +65,7559 @@ type GetInfoResponse struct { DefaultFiatCurrency string `protobuf:"bytes,5,opt,name=default_fiat_currency,json=defaultFiatCurrency,proto3" json:"default_fiat_currency,omitempty"` SubsystemStatus map[string]bool `protobuf:"bytes,6,rep,name=subsystem_status,json=subsystemStatus,proto3" json:"subsystem_status,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` RpcEndpoints map[string]*RPCEndpoint `protobuf:"bytes,7,rep,name=rpc_endpoints,json=rpcEndpoints,proto3" json:"rpc_endpoints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetInfoResponse) Reset() { - *x = GetInfoResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetInfoResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetInfoResponse) ProtoMessage() {} - -func (x *GetInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetInfoResponse.ProtoReflect.Descriptor instead. +func (m *GetInfoResponse) Reset() { *m = GetInfoResponse{} } +func (m *GetInfoResponse) String() string { return proto.CompactTextString(m) } +func (*GetInfoResponse) ProtoMessage() {} func (*GetInfoResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{1} + return fileDescriptor_77a6da22d6a3feb1, []int{1} } -func (x *GetInfoResponse) GetUptime() string { - if x != nil { - return x.Uptime +func (m *GetInfoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetInfoResponse.Unmarshal(m, b) +} +func (m *GetInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetInfoResponse.Marshal(b, m, deterministic) +} +func (m *GetInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetInfoResponse.Merge(m, src) +} +func (m *GetInfoResponse) XXX_Size() int { + return xxx_messageInfo_GetInfoResponse.Size(m) +} +func (m *GetInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetInfoResponse proto.InternalMessageInfo + +func (m *GetInfoResponse) GetUptime() string { + if m != nil { + return m.Uptime } return "" } -func (x *GetInfoResponse) GetAvailableExchanges() int64 { - if x != nil { - return x.AvailableExchanges +func (m *GetInfoResponse) GetAvailableExchanges() int64 { + if m != nil { + return m.AvailableExchanges } return 0 } -func (x *GetInfoResponse) GetEnabledExchanges() int64 { - if x != nil { - return x.EnabledExchanges +func (m *GetInfoResponse) GetEnabledExchanges() int64 { + if m != nil { + return m.EnabledExchanges } return 0 } -func (x *GetInfoResponse) GetDefaultForexProvider() string { - if x != nil { - return x.DefaultForexProvider +func (m *GetInfoResponse) GetDefaultForexProvider() string { + if m != nil { + return m.DefaultForexProvider } return "" } -func (x *GetInfoResponse) GetDefaultFiatCurrency() string { - if x != nil { - return x.DefaultFiatCurrency +func (m *GetInfoResponse) GetDefaultFiatCurrency() string { + if m != nil { + return m.DefaultFiatCurrency } return "" } -func (x *GetInfoResponse) GetSubsystemStatus() map[string]bool { - if x != nil { - return x.SubsystemStatus +func (m *GetInfoResponse) GetSubsystemStatus() map[string]bool { + if m != nil { + return m.SubsystemStatus } return nil } -func (x *GetInfoResponse) GetRpcEndpoints() map[string]*RPCEndpoint { - if x != nil { - return x.RpcEndpoints +func (m *GetInfoResponse) GetRpcEndpoints() map[string]*RPCEndpoint { + if m != nil { + return m.RpcEndpoints } return nil } type GetCommunicationRelayersRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetCommunicationRelayersRequest) Reset() { - *x = GetCommunicationRelayersRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetCommunicationRelayersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCommunicationRelayersRequest) ProtoMessage() {} - -func (x *GetCommunicationRelayersRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCommunicationRelayersRequest.ProtoReflect.Descriptor instead. +func (m *GetCommunicationRelayersRequest) Reset() { *m = GetCommunicationRelayersRequest{} } +func (m *GetCommunicationRelayersRequest) String() string { return proto.CompactTextString(m) } +func (*GetCommunicationRelayersRequest) ProtoMessage() {} func (*GetCommunicationRelayersRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{2} + return fileDescriptor_77a6da22d6a3feb1, []int{2} } +func (m *GetCommunicationRelayersRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCommunicationRelayersRequest.Unmarshal(m, b) +} +func (m *GetCommunicationRelayersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCommunicationRelayersRequest.Marshal(b, m, deterministic) +} +func (m *GetCommunicationRelayersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCommunicationRelayersRequest.Merge(m, src) +} +func (m *GetCommunicationRelayersRequest) XXX_Size() int { + return xxx_messageInfo_GetCommunicationRelayersRequest.Size(m) +} +func (m *GetCommunicationRelayersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetCommunicationRelayersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCommunicationRelayersRequest proto.InternalMessageInfo + type CommunicationRelayer struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - Connected bool `protobuf:"varint,2,opt,name=connected,proto3" json:"connected,omitempty"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Connected bool `protobuf:"varint,2,opt,name=connected,proto3" json:"connected,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *CommunicationRelayer) Reset() { - *x = CommunicationRelayer{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CommunicationRelayer) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CommunicationRelayer) ProtoMessage() {} - -func (x *CommunicationRelayer) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CommunicationRelayer.ProtoReflect.Descriptor instead. +func (m *CommunicationRelayer) Reset() { *m = CommunicationRelayer{} } +func (m *CommunicationRelayer) String() string { return proto.CompactTextString(m) } +func (*CommunicationRelayer) ProtoMessage() {} func (*CommunicationRelayer) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{3} + return fileDescriptor_77a6da22d6a3feb1, []int{3} } -func (x *CommunicationRelayer) GetEnabled() bool { - if x != nil { - return x.Enabled +func (m *CommunicationRelayer) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CommunicationRelayer.Unmarshal(m, b) +} +func (m *CommunicationRelayer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CommunicationRelayer.Marshal(b, m, deterministic) +} +func (m *CommunicationRelayer) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommunicationRelayer.Merge(m, src) +} +func (m *CommunicationRelayer) XXX_Size() int { + return xxx_messageInfo_CommunicationRelayer.Size(m) +} +func (m *CommunicationRelayer) XXX_DiscardUnknown() { + xxx_messageInfo_CommunicationRelayer.DiscardUnknown(m) +} + +var xxx_messageInfo_CommunicationRelayer proto.InternalMessageInfo + +func (m *CommunicationRelayer) GetEnabled() bool { + if m != nil { + return m.Enabled } return false } -func (x *CommunicationRelayer) GetConnected() bool { - if x != nil { - return x.Connected +func (m *CommunicationRelayer) GetConnected() bool { + if m != nil { + return m.Connected } return false } type GetCommunicationRelayersResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - CommunicationRelayers map[string]*CommunicationRelayer `protobuf:"bytes,1,rep,name=communication_relayers,json=communicationRelayers,proto3" json:"communication_relayers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetCommunicationRelayersResponse) Reset() { - *x = GetCommunicationRelayersResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetCommunicationRelayersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCommunicationRelayersResponse) ProtoMessage() {} - -func (x *GetCommunicationRelayersResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCommunicationRelayersResponse.ProtoReflect.Descriptor instead. +func (m *GetCommunicationRelayersResponse) Reset() { *m = GetCommunicationRelayersResponse{} } +func (m *GetCommunicationRelayersResponse) String() string { return proto.CompactTextString(m) } +func (*GetCommunicationRelayersResponse) ProtoMessage() {} func (*GetCommunicationRelayersResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{4} + return fileDescriptor_77a6da22d6a3feb1, []int{4} } -func (x *GetCommunicationRelayersResponse) GetCommunicationRelayers() map[string]*CommunicationRelayer { - if x != nil { - return x.CommunicationRelayers +func (m *GetCommunicationRelayersResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCommunicationRelayersResponse.Unmarshal(m, b) +} +func (m *GetCommunicationRelayersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCommunicationRelayersResponse.Marshal(b, m, deterministic) +} +func (m *GetCommunicationRelayersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCommunicationRelayersResponse.Merge(m, src) +} +func (m *GetCommunicationRelayersResponse) XXX_Size() int { + return xxx_messageInfo_GetCommunicationRelayersResponse.Size(m) +} +func (m *GetCommunicationRelayersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetCommunicationRelayersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCommunicationRelayersResponse proto.InternalMessageInfo + +func (m *GetCommunicationRelayersResponse) GetCommunicationRelayers() map[string]*CommunicationRelayer { + if m != nil { + return m.CommunicationRelayers } return nil } type GenericSubsystemRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Subsystem string `protobuf:"bytes,1,opt,name=subsystem,proto3" json:"subsystem,omitempty"` + Subsystem string `protobuf:"bytes,1,opt,name=subsystem,proto3" json:"subsystem,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GenericSubsystemRequest) Reset() { - *x = GenericSubsystemRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GenericSubsystemRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GenericSubsystemRequest) ProtoMessage() {} - -func (x *GenericSubsystemRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GenericSubsystemRequest.ProtoReflect.Descriptor instead. +func (m *GenericSubsystemRequest) Reset() { *m = GenericSubsystemRequest{} } +func (m *GenericSubsystemRequest) String() string { return proto.CompactTextString(m) } +func (*GenericSubsystemRequest) ProtoMessage() {} func (*GenericSubsystemRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{5} + return fileDescriptor_77a6da22d6a3feb1, []int{5} } -func (x *GenericSubsystemRequest) GetSubsystem() string { - if x != nil { - return x.Subsystem +func (m *GenericSubsystemRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GenericSubsystemRequest.Unmarshal(m, b) +} +func (m *GenericSubsystemRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GenericSubsystemRequest.Marshal(b, m, deterministic) +} +func (m *GenericSubsystemRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenericSubsystemRequest.Merge(m, src) +} +func (m *GenericSubsystemRequest) XXX_Size() int { + return xxx_messageInfo_GenericSubsystemRequest.Size(m) +} +func (m *GenericSubsystemRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GenericSubsystemRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GenericSubsystemRequest proto.InternalMessageInfo + +func (m *GenericSubsystemRequest) GetSubsystem() string { + if m != nil { + return m.Subsystem } return "" } -type GenericSubsystemResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *GenericSubsystemResponse) Reset() { - *x = GenericSubsystemResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GenericSubsystemResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GenericSubsystemResponse) ProtoMessage() {} - -func (x *GenericSubsystemResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GenericSubsystemResponse.ProtoReflect.Descriptor instead. -func (*GenericSubsystemResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{6} -} - type GetSubsystemsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetSubsystemsRequest) Reset() { - *x = GetSubsystemsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetSubsystemsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSubsystemsRequest) ProtoMessage() {} - -func (x *GetSubsystemsRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSubsystemsRequest.ProtoReflect.Descriptor instead. +func (m *GetSubsystemsRequest) Reset() { *m = GetSubsystemsRequest{} } +func (m *GetSubsystemsRequest) String() string { return proto.CompactTextString(m) } +func (*GetSubsystemsRequest) ProtoMessage() {} func (*GetSubsystemsRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{7} + return fileDescriptor_77a6da22d6a3feb1, []int{6} } +func (m *GetSubsystemsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSubsystemsRequest.Unmarshal(m, b) +} +func (m *GetSubsystemsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSubsystemsRequest.Marshal(b, m, deterministic) +} +func (m *GetSubsystemsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSubsystemsRequest.Merge(m, src) +} +func (m *GetSubsystemsRequest) XXX_Size() int { + return xxx_messageInfo_GetSubsystemsRequest.Size(m) +} +func (m *GetSubsystemsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetSubsystemsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSubsystemsRequest proto.InternalMessageInfo + type GetSusbsytemsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SubsystemsStatus map[string]bool `protobuf:"bytes,1,rep,name=subsystems_status,json=subsystemsStatus,proto3" json:"subsystems_status,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + SubsystemsStatus map[string]bool `protobuf:"bytes,1,rep,name=subsystems_status,json=subsystemsStatus,proto3" json:"subsystems_status,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetSusbsytemsResponse) Reset() { - *x = GetSusbsytemsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetSusbsytemsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSusbsytemsResponse) ProtoMessage() {} - -func (x *GetSusbsytemsResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSusbsytemsResponse.ProtoReflect.Descriptor instead. +func (m *GetSusbsytemsResponse) Reset() { *m = GetSusbsytemsResponse{} } +func (m *GetSusbsytemsResponse) String() string { return proto.CompactTextString(m) } +func (*GetSusbsytemsResponse) ProtoMessage() {} func (*GetSusbsytemsResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{8} + return fileDescriptor_77a6da22d6a3feb1, []int{7} } -func (x *GetSusbsytemsResponse) GetSubsystemsStatus() map[string]bool { - if x != nil { - return x.SubsystemsStatus +func (m *GetSusbsytemsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetSusbsytemsResponse.Unmarshal(m, b) +} +func (m *GetSusbsytemsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetSusbsytemsResponse.Marshal(b, m, deterministic) +} +func (m *GetSusbsytemsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetSusbsytemsResponse.Merge(m, src) +} +func (m *GetSusbsytemsResponse) XXX_Size() int { + return xxx_messageInfo_GetSusbsytemsResponse.Size(m) +} +func (m *GetSusbsytemsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetSusbsytemsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetSusbsytemsResponse proto.InternalMessageInfo + +func (m *GetSusbsytemsResponse) GetSubsystemsStatus() map[string]bool { + if m != nil { + return m.SubsystemsStatus } return nil } type GetRPCEndpointsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetRPCEndpointsRequest) Reset() { - *x = GetRPCEndpointsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetRPCEndpointsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetRPCEndpointsRequest) ProtoMessage() {} - -func (x *GetRPCEndpointsRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetRPCEndpointsRequest.ProtoReflect.Descriptor instead. +func (m *GetRPCEndpointsRequest) Reset() { *m = GetRPCEndpointsRequest{} } +func (m *GetRPCEndpointsRequest) String() string { return proto.CompactTextString(m) } +func (*GetRPCEndpointsRequest) ProtoMessage() {} func (*GetRPCEndpointsRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{9} + return fileDescriptor_77a6da22d6a3feb1, []int{8} } +func (m *GetRPCEndpointsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetRPCEndpointsRequest.Unmarshal(m, b) +} +func (m *GetRPCEndpointsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetRPCEndpointsRequest.Marshal(b, m, deterministic) +} +func (m *GetRPCEndpointsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetRPCEndpointsRequest.Merge(m, src) +} +func (m *GetRPCEndpointsRequest) XXX_Size() int { + return xxx_messageInfo_GetRPCEndpointsRequest.Size(m) +} +func (m *GetRPCEndpointsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetRPCEndpointsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetRPCEndpointsRequest proto.InternalMessageInfo + type RPCEndpoint struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Started bool `protobuf:"varint,1,opt,name=started,proto3" json:"started,omitempty"` - ListenAddress string `protobuf:"bytes,2,opt,name=listen_address,json=listenAddress,proto3" json:"listen_address,omitempty"` + Started bool `protobuf:"varint,1,opt,name=started,proto3" json:"started,omitempty"` + ListenAddress string `protobuf:"bytes,2,opt,name=listen_address,json=listenAddress,proto3" json:"listen_address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *RPCEndpoint) Reset() { - *x = RPCEndpoint{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RPCEndpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RPCEndpoint) ProtoMessage() {} - -func (x *RPCEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RPCEndpoint.ProtoReflect.Descriptor instead. +func (m *RPCEndpoint) Reset() { *m = RPCEndpoint{} } +func (m *RPCEndpoint) String() string { return proto.CompactTextString(m) } +func (*RPCEndpoint) ProtoMessage() {} func (*RPCEndpoint) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{10} + return fileDescriptor_77a6da22d6a3feb1, []int{9} } -func (x *RPCEndpoint) GetStarted() bool { - if x != nil { - return x.Started +func (m *RPCEndpoint) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RPCEndpoint.Unmarshal(m, b) +} +func (m *RPCEndpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RPCEndpoint.Marshal(b, m, deterministic) +} +func (m *RPCEndpoint) XXX_Merge(src proto.Message) { + xxx_messageInfo_RPCEndpoint.Merge(m, src) +} +func (m *RPCEndpoint) XXX_Size() int { + return xxx_messageInfo_RPCEndpoint.Size(m) +} +func (m *RPCEndpoint) XXX_DiscardUnknown() { + xxx_messageInfo_RPCEndpoint.DiscardUnknown(m) +} + +var xxx_messageInfo_RPCEndpoint proto.InternalMessageInfo + +func (m *RPCEndpoint) GetStarted() bool { + if m != nil { + return m.Started } return false } -func (x *RPCEndpoint) GetListenAddress() string { - if x != nil { - return x.ListenAddress +func (m *RPCEndpoint) GetListenAddress() string { + if m != nil { + return m.ListenAddress } return "" } type GetRPCEndpointsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Endpoints map[string]*RPCEndpoint `protobuf:"bytes,1,rep,name=endpoints,proto3" json:"endpoints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Endpoints map[string]*RPCEndpoint `protobuf:"bytes,1,rep,name=endpoints,proto3" json:"endpoints,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetRPCEndpointsResponse) Reset() { - *x = GetRPCEndpointsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetRPCEndpointsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetRPCEndpointsResponse) ProtoMessage() {} - -func (x *GetRPCEndpointsResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetRPCEndpointsResponse.ProtoReflect.Descriptor instead. +func (m *GetRPCEndpointsResponse) Reset() { *m = GetRPCEndpointsResponse{} } +func (m *GetRPCEndpointsResponse) String() string { return proto.CompactTextString(m) } +func (*GetRPCEndpointsResponse) ProtoMessage() {} func (*GetRPCEndpointsResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{11} + return fileDescriptor_77a6da22d6a3feb1, []int{10} } -func (x *GetRPCEndpointsResponse) GetEndpoints() map[string]*RPCEndpoint { - if x != nil { - return x.Endpoints +func (m *GetRPCEndpointsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetRPCEndpointsResponse.Unmarshal(m, b) +} +func (m *GetRPCEndpointsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetRPCEndpointsResponse.Marshal(b, m, deterministic) +} +func (m *GetRPCEndpointsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetRPCEndpointsResponse.Merge(m, src) +} +func (m *GetRPCEndpointsResponse) XXX_Size() int { + return xxx_messageInfo_GetRPCEndpointsResponse.Size(m) +} +func (m *GetRPCEndpointsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetRPCEndpointsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetRPCEndpointsResponse proto.InternalMessageInfo + +func (m *GetRPCEndpointsResponse) GetEndpoints() map[string]*RPCEndpoint { + if m != nil { + return m.Endpoints } return nil } type GenericExchangeNameRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GenericExchangeNameRequest) Reset() { - *x = GenericExchangeNameRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GenericExchangeNameRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GenericExchangeNameRequest) ProtoMessage() {} - -func (x *GenericExchangeNameRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GenericExchangeNameRequest.ProtoReflect.Descriptor instead. +func (m *GenericExchangeNameRequest) Reset() { *m = GenericExchangeNameRequest{} } +func (m *GenericExchangeNameRequest) String() string { return proto.CompactTextString(m) } +func (*GenericExchangeNameRequest) ProtoMessage() {} func (*GenericExchangeNameRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{12} + return fileDescriptor_77a6da22d6a3feb1, []int{11} } -func (x *GenericExchangeNameRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GenericExchangeNameRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GenericExchangeNameRequest.Unmarshal(m, b) +} +func (m *GenericExchangeNameRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GenericExchangeNameRequest.Marshal(b, m, deterministic) +} +func (m *GenericExchangeNameRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenericExchangeNameRequest.Merge(m, src) +} +func (m *GenericExchangeNameRequest) XXX_Size() int { + return xxx_messageInfo_GenericExchangeNameRequest.Size(m) +} +func (m *GenericExchangeNameRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GenericExchangeNameRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GenericExchangeNameRequest proto.InternalMessageInfo + +func (m *GenericExchangeNameRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -type GenericExchangeNameResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *GenericExchangeNameResponse) Reset() { - *x = GenericExchangeNameResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GenericExchangeNameResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GenericExchangeNameResponse) ProtoMessage() {} - -func (x *GenericExchangeNameResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GenericExchangeNameResponse.ProtoReflect.Descriptor instead. -func (*GenericExchangeNameResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{13} -} - type GetExchangesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangesRequest) Reset() { - *x = GetExchangesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangesRequest) ProtoMessage() {} - -func (x *GetExchangesRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangesRequest.ProtoReflect.Descriptor instead. +func (m *GetExchangesRequest) Reset() { *m = GetExchangesRequest{} } +func (m *GetExchangesRequest) String() string { return proto.CompactTextString(m) } +func (*GetExchangesRequest) ProtoMessage() {} func (*GetExchangesRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{14} + return fileDescriptor_77a6da22d6a3feb1, []int{12} } -func (x *GetExchangesRequest) GetEnabled() bool { - if x != nil { - return x.Enabled +func (m *GetExchangesRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangesRequest.Unmarshal(m, b) +} +func (m *GetExchangesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangesRequest.Marshal(b, m, deterministic) +} +func (m *GetExchangesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangesRequest.Merge(m, src) +} +func (m *GetExchangesRequest) XXX_Size() int { + return xxx_messageInfo_GetExchangesRequest.Size(m) +} +func (m *GetExchangesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangesRequest proto.InternalMessageInfo + +func (m *GetExchangesRequest) GetEnabled() bool { + if m != nil { + return m.Enabled } return false } type GetExchangesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchanges string `protobuf:"bytes,1,opt,name=exchanges,proto3" json:"exchanges,omitempty"` + Exchanges string `protobuf:"bytes,1,opt,name=exchanges,proto3" json:"exchanges,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangesResponse) Reset() { - *x = GetExchangesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangesResponse) ProtoMessage() {} - -func (x *GetExchangesResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangesResponse.ProtoReflect.Descriptor instead. +func (m *GetExchangesResponse) Reset() { *m = GetExchangesResponse{} } +func (m *GetExchangesResponse) String() string { return proto.CompactTextString(m) } +func (*GetExchangesResponse) ProtoMessage() {} func (*GetExchangesResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{15} + return fileDescriptor_77a6da22d6a3feb1, []int{13} } -func (x *GetExchangesResponse) GetExchanges() string { - if x != nil { - return x.Exchanges +func (m *GetExchangesResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangesResponse.Unmarshal(m, b) +} +func (m *GetExchangesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangesResponse.Marshal(b, m, deterministic) +} +func (m *GetExchangesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangesResponse.Merge(m, src) +} +func (m *GetExchangesResponse) XXX_Size() int { + return xxx_messageInfo_GetExchangesResponse.Size(m) +} +func (m *GetExchangesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangesResponse proto.InternalMessageInfo + +func (m *GetExchangesResponse) GetExchanges() string { + if m != nil { + return m.Exchanges } return "" } type GetExchangeOTPReponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - OtpCode string `protobuf:"bytes,1,opt,name=otp_code,json=otpCode,proto3" json:"otp_code,omitempty"` + OtpCode string `protobuf:"bytes,1,opt,name=otp_code,json=otpCode,proto3" json:"otp_code,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangeOTPReponse) Reset() { - *x = GetExchangeOTPReponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangeOTPReponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangeOTPReponse) ProtoMessage() {} - -func (x *GetExchangeOTPReponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangeOTPReponse.ProtoReflect.Descriptor instead. +func (m *GetExchangeOTPReponse) Reset() { *m = GetExchangeOTPReponse{} } +func (m *GetExchangeOTPReponse) String() string { return proto.CompactTextString(m) } +func (*GetExchangeOTPReponse) ProtoMessage() {} func (*GetExchangeOTPReponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{16} + return fileDescriptor_77a6da22d6a3feb1, []int{14} } -func (x *GetExchangeOTPReponse) GetOtpCode() string { - if x != nil { - return x.OtpCode +func (m *GetExchangeOTPReponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeOTPReponse.Unmarshal(m, b) +} +func (m *GetExchangeOTPReponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeOTPReponse.Marshal(b, m, deterministic) +} +func (m *GetExchangeOTPReponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeOTPReponse.Merge(m, src) +} +func (m *GetExchangeOTPReponse) XXX_Size() int { + return xxx_messageInfo_GetExchangeOTPReponse.Size(m) +} +func (m *GetExchangeOTPReponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeOTPReponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeOTPReponse proto.InternalMessageInfo + +func (m *GetExchangeOTPReponse) GetOtpCode() string { + if m != nil { + return m.OtpCode } return "" } type GetExchangeOTPsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangeOTPsRequest) Reset() { - *x = GetExchangeOTPsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangeOTPsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangeOTPsRequest) ProtoMessage() {} - -func (x *GetExchangeOTPsRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[17] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangeOTPsRequest.ProtoReflect.Descriptor instead. +func (m *GetExchangeOTPsRequest) Reset() { *m = GetExchangeOTPsRequest{} } +func (m *GetExchangeOTPsRequest) String() string { return proto.CompactTextString(m) } +func (*GetExchangeOTPsRequest) ProtoMessage() {} func (*GetExchangeOTPsRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{17} + return fileDescriptor_77a6da22d6a3feb1, []int{15} } +func (m *GetExchangeOTPsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeOTPsRequest.Unmarshal(m, b) +} +func (m *GetExchangeOTPsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeOTPsRequest.Marshal(b, m, deterministic) +} +func (m *GetExchangeOTPsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeOTPsRequest.Merge(m, src) +} +func (m *GetExchangeOTPsRequest) XXX_Size() int { + return xxx_messageInfo_GetExchangeOTPsRequest.Size(m) +} +func (m *GetExchangeOTPsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeOTPsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeOTPsRequest proto.InternalMessageInfo + type GetExchangeOTPsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - OtpCodes map[string]string `protobuf:"bytes,1,rep,name=otp_codes,json=otpCodes,proto3" json:"otp_codes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + OtpCodes map[string]string `protobuf:"bytes,1,rep,name=otp_codes,json=otpCodes,proto3" json:"otp_codes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangeOTPsResponse) Reset() { - *x = GetExchangeOTPsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangeOTPsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangeOTPsResponse) ProtoMessage() {} - -func (x *GetExchangeOTPsResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[18] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangeOTPsResponse.ProtoReflect.Descriptor instead. +func (m *GetExchangeOTPsResponse) Reset() { *m = GetExchangeOTPsResponse{} } +func (m *GetExchangeOTPsResponse) String() string { return proto.CompactTextString(m) } +func (*GetExchangeOTPsResponse) ProtoMessage() {} func (*GetExchangeOTPsResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{18} + return fileDescriptor_77a6da22d6a3feb1, []int{16} } -func (x *GetExchangeOTPsResponse) GetOtpCodes() map[string]string { - if x != nil { - return x.OtpCodes +func (m *GetExchangeOTPsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeOTPsResponse.Unmarshal(m, b) +} +func (m *GetExchangeOTPsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeOTPsResponse.Marshal(b, m, deterministic) +} +func (m *GetExchangeOTPsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeOTPsResponse.Merge(m, src) +} +func (m *GetExchangeOTPsResponse) XXX_Size() int { + return xxx_messageInfo_GetExchangeOTPsResponse.Size(m) +} +func (m *GetExchangeOTPsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeOTPsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeOTPsResponse proto.InternalMessageInfo + +func (m *GetExchangeOTPsResponse) GetOtpCodes() map[string]string { + if m != nil { + return m.OtpCodes } return nil } type DisableExchangeRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *DisableExchangeRequest) Reset() { - *x = DisableExchangeRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DisableExchangeRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DisableExchangeRequest) ProtoMessage() {} - -func (x *DisableExchangeRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[19] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DisableExchangeRequest.ProtoReflect.Descriptor instead. +func (m *DisableExchangeRequest) Reset() { *m = DisableExchangeRequest{} } +func (m *DisableExchangeRequest) String() string { return proto.CompactTextString(m) } +func (*DisableExchangeRequest) ProtoMessage() {} func (*DisableExchangeRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{19} + return fileDescriptor_77a6da22d6a3feb1, []int{17} } -func (x *DisableExchangeRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *DisableExchangeRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DisableExchangeRequest.Unmarshal(m, b) +} +func (m *DisableExchangeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DisableExchangeRequest.Marshal(b, m, deterministic) +} +func (m *DisableExchangeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_DisableExchangeRequest.Merge(m, src) +} +func (m *DisableExchangeRequest) XXX_Size() int { + return xxx_messageInfo_DisableExchangeRequest.Size(m) +} +func (m *DisableExchangeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_DisableExchangeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_DisableExchangeRequest proto.InternalMessageInfo + +func (m *DisableExchangeRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } type PairsSupported struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - AvailablePairs string `protobuf:"bytes,1,opt,name=available_pairs,json=availablePairs,proto3" json:"available_pairs,omitempty"` - EnabledPairs string `protobuf:"bytes,2,opt,name=enabled_pairs,json=enabledPairs,proto3" json:"enabled_pairs,omitempty"` + AvailablePairs string `protobuf:"bytes,1,opt,name=available_pairs,json=availablePairs,proto3" json:"available_pairs,omitempty"` + EnabledPairs string `protobuf:"bytes,2,opt,name=enabled_pairs,json=enabledPairs,proto3" json:"enabled_pairs,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *PairsSupported) Reset() { - *x = PairsSupported{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PairsSupported) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PairsSupported) ProtoMessage() {} - -func (x *PairsSupported) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[20] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PairsSupported.ProtoReflect.Descriptor instead. +func (m *PairsSupported) Reset() { *m = PairsSupported{} } +func (m *PairsSupported) String() string { return proto.CompactTextString(m) } +func (*PairsSupported) ProtoMessage() {} func (*PairsSupported) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{20} + return fileDescriptor_77a6da22d6a3feb1, []int{18} } -func (x *PairsSupported) GetAvailablePairs() string { - if x != nil { - return x.AvailablePairs +func (m *PairsSupported) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PairsSupported.Unmarshal(m, b) +} +func (m *PairsSupported) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PairsSupported.Marshal(b, m, deterministic) +} +func (m *PairsSupported) XXX_Merge(src proto.Message) { + xxx_messageInfo_PairsSupported.Merge(m, src) +} +func (m *PairsSupported) XXX_Size() int { + return xxx_messageInfo_PairsSupported.Size(m) +} +func (m *PairsSupported) XXX_DiscardUnknown() { + xxx_messageInfo_PairsSupported.DiscardUnknown(m) +} + +var xxx_messageInfo_PairsSupported proto.InternalMessageInfo + +func (m *PairsSupported) GetAvailablePairs() string { + if m != nil { + return m.AvailablePairs } return "" } -func (x *PairsSupported) GetEnabledPairs() string { - if x != nil { - return x.EnabledPairs +func (m *PairsSupported) GetEnabledPairs() string { + if m != nil { + return m.EnabledPairs } return "" } type GetExchangeInfoResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` - Verbose bool `protobuf:"varint,3,opt,name=verbose,proto3" json:"verbose,omitempty"` - UsingSandbox bool `protobuf:"varint,4,opt,name=using_sandbox,json=usingSandbox,proto3" json:"using_sandbox,omitempty"` - HttpTimeout string `protobuf:"bytes,5,opt,name=http_timeout,json=httpTimeout,proto3" json:"http_timeout,omitempty"` - HttpUseragent string `protobuf:"bytes,6,opt,name=http_useragent,json=httpUseragent,proto3" json:"http_useragent,omitempty"` - HttpProxy string `protobuf:"bytes,7,opt,name=http_proxy,json=httpProxy,proto3" json:"http_proxy,omitempty"` - BaseCurrencies string `protobuf:"bytes,8,opt,name=base_currencies,json=baseCurrencies,proto3" json:"base_currencies,omitempty"` - SupportedAssets map[string]*PairsSupported `protobuf:"bytes,9,rep,name=supported_assets,json=supportedAssets,proto3" json:"supported_assets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - AuthenticatedApi bool `protobuf:"varint,10,opt,name=authenticated_api,json=authenticatedApi,proto3" json:"authenticated_api,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + Verbose bool `protobuf:"varint,3,opt,name=verbose,proto3" json:"verbose,omitempty"` + UsingSandbox bool `protobuf:"varint,4,opt,name=using_sandbox,json=usingSandbox,proto3" json:"using_sandbox,omitempty"` + HttpTimeout string `protobuf:"bytes,5,opt,name=http_timeout,json=httpTimeout,proto3" json:"http_timeout,omitempty"` + HttpUseragent string `protobuf:"bytes,6,opt,name=http_useragent,json=httpUseragent,proto3" json:"http_useragent,omitempty"` + HttpProxy string `protobuf:"bytes,7,opt,name=http_proxy,json=httpProxy,proto3" json:"http_proxy,omitempty"` + BaseCurrencies string `protobuf:"bytes,8,opt,name=base_currencies,json=baseCurrencies,proto3" json:"base_currencies,omitempty"` + SupportedAssets map[string]*PairsSupported `protobuf:"bytes,9,rep,name=supported_assets,json=supportedAssets,proto3" json:"supported_assets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + AuthenticatedApi bool `protobuf:"varint,10,opt,name=authenticated_api,json=authenticatedApi,proto3" json:"authenticated_api,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangeInfoResponse) Reset() { - *x = GetExchangeInfoResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangeInfoResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangeInfoResponse) ProtoMessage() {} - -func (x *GetExchangeInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[21] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangeInfoResponse.ProtoReflect.Descriptor instead. +func (m *GetExchangeInfoResponse) Reset() { *m = GetExchangeInfoResponse{} } +func (m *GetExchangeInfoResponse) String() string { return proto.CompactTextString(m) } +func (*GetExchangeInfoResponse) ProtoMessage() {} func (*GetExchangeInfoResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{21} + return fileDescriptor_77a6da22d6a3feb1, []int{19} } -func (x *GetExchangeInfoResponse) GetName() string { - if x != nil { - return x.Name +func (m *GetExchangeInfoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeInfoResponse.Unmarshal(m, b) +} +func (m *GetExchangeInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeInfoResponse.Marshal(b, m, deterministic) +} +func (m *GetExchangeInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeInfoResponse.Merge(m, src) +} +func (m *GetExchangeInfoResponse) XXX_Size() int { + return xxx_messageInfo_GetExchangeInfoResponse.Size(m) +} +func (m *GetExchangeInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeInfoResponse proto.InternalMessageInfo + +func (m *GetExchangeInfoResponse) GetName() string { + if m != nil { + return m.Name } return "" } -func (x *GetExchangeInfoResponse) GetEnabled() bool { - if x != nil { - return x.Enabled +func (m *GetExchangeInfoResponse) GetEnabled() bool { + if m != nil { + return m.Enabled } return false } -func (x *GetExchangeInfoResponse) GetVerbose() bool { - if x != nil { - return x.Verbose +func (m *GetExchangeInfoResponse) GetVerbose() bool { + if m != nil { + return m.Verbose } return false } -func (x *GetExchangeInfoResponse) GetUsingSandbox() bool { - if x != nil { - return x.UsingSandbox +func (m *GetExchangeInfoResponse) GetUsingSandbox() bool { + if m != nil { + return m.UsingSandbox } return false } -func (x *GetExchangeInfoResponse) GetHttpTimeout() string { - if x != nil { - return x.HttpTimeout +func (m *GetExchangeInfoResponse) GetHttpTimeout() string { + if m != nil { + return m.HttpTimeout } return "" } -func (x *GetExchangeInfoResponse) GetHttpUseragent() string { - if x != nil { - return x.HttpUseragent +func (m *GetExchangeInfoResponse) GetHttpUseragent() string { + if m != nil { + return m.HttpUseragent } return "" } -func (x *GetExchangeInfoResponse) GetHttpProxy() string { - if x != nil { - return x.HttpProxy +func (m *GetExchangeInfoResponse) GetHttpProxy() string { + if m != nil { + return m.HttpProxy } return "" } -func (x *GetExchangeInfoResponse) GetBaseCurrencies() string { - if x != nil { - return x.BaseCurrencies +func (m *GetExchangeInfoResponse) GetBaseCurrencies() string { + if m != nil { + return m.BaseCurrencies } return "" } -func (x *GetExchangeInfoResponse) GetSupportedAssets() map[string]*PairsSupported { - if x != nil { - return x.SupportedAssets +func (m *GetExchangeInfoResponse) GetSupportedAssets() map[string]*PairsSupported { + if m != nil { + return m.SupportedAssets } return nil } -func (x *GetExchangeInfoResponse) GetAuthenticatedApi() bool { - if x != nil { - return x.AuthenticatedApi +func (m *GetExchangeInfoResponse) GetAuthenticatedApi() bool { + if m != nil { + return m.AuthenticatedApi } return false } type GetTickerRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetTickerRequest) Reset() { - *x = GetTickerRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetTickerRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetTickerRequest) ProtoMessage() {} - -func (x *GetTickerRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[22] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetTickerRequest.ProtoReflect.Descriptor instead. +func (m *GetTickerRequest) Reset() { *m = GetTickerRequest{} } +func (m *GetTickerRequest) String() string { return proto.CompactTextString(m) } +func (*GetTickerRequest) ProtoMessage() {} func (*GetTickerRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{22} + return fileDescriptor_77a6da22d6a3feb1, []int{20} } -func (x *GetTickerRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetTickerRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTickerRequest.Unmarshal(m, b) +} +func (m *GetTickerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTickerRequest.Marshal(b, m, deterministic) +} +func (m *GetTickerRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTickerRequest.Merge(m, src) +} +func (m *GetTickerRequest) XXX_Size() int { + return xxx_messageInfo_GetTickerRequest.Size(m) +} +func (m *GetTickerRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTickerRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTickerRequest proto.InternalMessageInfo + +func (m *GetTickerRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetTickerRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *GetTickerRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *GetTickerRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *GetTickerRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } type CurrencyPair struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Delimiter string `protobuf:"bytes,1,opt,name=delimiter,proto3" json:"delimiter,omitempty"` - Base string `protobuf:"bytes,2,opt,name=base,proto3" json:"base,omitempty"` - Quote string `protobuf:"bytes,3,opt,name=quote,proto3" json:"quote,omitempty"` + Delimiter string `protobuf:"bytes,1,opt,name=delimiter,proto3" json:"delimiter,omitempty"` + Base string `protobuf:"bytes,2,opt,name=base,proto3" json:"base,omitempty"` + Quote string `protobuf:"bytes,3,opt,name=quote,proto3" json:"quote,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *CurrencyPair) Reset() { - *x = CurrencyPair{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CurrencyPair) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CurrencyPair) ProtoMessage() {} - -func (x *CurrencyPair) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[23] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CurrencyPair.ProtoReflect.Descriptor instead. +func (m *CurrencyPair) Reset() { *m = CurrencyPair{} } +func (m *CurrencyPair) String() string { return proto.CompactTextString(m) } +func (*CurrencyPair) ProtoMessage() {} func (*CurrencyPair) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{23} + return fileDescriptor_77a6da22d6a3feb1, []int{21} } -func (x *CurrencyPair) GetDelimiter() string { - if x != nil { - return x.Delimiter +func (m *CurrencyPair) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CurrencyPair.Unmarshal(m, b) +} +func (m *CurrencyPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CurrencyPair.Marshal(b, m, deterministic) +} +func (m *CurrencyPair) XXX_Merge(src proto.Message) { + xxx_messageInfo_CurrencyPair.Merge(m, src) +} +func (m *CurrencyPair) XXX_Size() int { + return xxx_messageInfo_CurrencyPair.Size(m) +} +func (m *CurrencyPair) XXX_DiscardUnknown() { + xxx_messageInfo_CurrencyPair.DiscardUnknown(m) +} + +var xxx_messageInfo_CurrencyPair proto.InternalMessageInfo + +func (m *CurrencyPair) GetDelimiter() string { + if m != nil { + return m.Delimiter } return "" } -func (x *CurrencyPair) GetBase() string { - if x != nil { - return x.Base +func (m *CurrencyPair) GetBase() string { + if m != nil { + return m.Base } return "" } -func (x *CurrencyPair) GetQuote() string { - if x != nil { - return x.Quote +func (m *CurrencyPair) GetQuote() string { + if m != nil { + return m.Quote } return "" } type TickerResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Pair *CurrencyPair `protobuf:"bytes,1,opt,name=pair,proto3" json:"pair,omitempty"` - LastUpdated int64 `protobuf:"varint,2,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"` - CurrencyPair string `protobuf:"bytes,3,opt,name=currency_pair,json=currencyPair,proto3" json:"currency_pair,omitempty"` - Last float64 `protobuf:"fixed64,4,opt,name=last,proto3" json:"last,omitempty"` - High float64 `protobuf:"fixed64,5,opt,name=high,proto3" json:"high,omitempty"` - Low float64 `protobuf:"fixed64,6,opt,name=low,proto3" json:"low,omitempty"` - Bid float64 `protobuf:"fixed64,7,opt,name=bid,proto3" json:"bid,omitempty"` - Ask float64 `protobuf:"fixed64,8,opt,name=ask,proto3" json:"ask,omitempty"` - Volume float64 `protobuf:"fixed64,9,opt,name=volume,proto3" json:"volume,omitempty"` - PriceAth float64 `protobuf:"fixed64,10,opt,name=price_ath,json=priceAth,proto3" json:"price_ath,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,1,opt,name=pair,proto3" json:"pair,omitempty"` + LastUpdated int64 `protobuf:"varint,2,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"` + CurrencyPair string `protobuf:"bytes,3,opt,name=currency_pair,json=currencyPair,proto3" json:"currency_pair,omitempty"` + Last float64 `protobuf:"fixed64,4,opt,name=last,proto3" json:"last,omitempty"` + High float64 `protobuf:"fixed64,5,opt,name=high,proto3" json:"high,omitempty"` + Low float64 `protobuf:"fixed64,6,opt,name=low,proto3" json:"low,omitempty"` + Bid float64 `protobuf:"fixed64,7,opt,name=bid,proto3" json:"bid,omitempty"` + Ask float64 `protobuf:"fixed64,8,opt,name=ask,proto3" json:"ask,omitempty"` + Volume float64 `protobuf:"fixed64,9,opt,name=volume,proto3" json:"volume,omitempty"` + PriceAth float64 `protobuf:"fixed64,10,opt,name=price_ath,json=priceAth,proto3" json:"price_ath,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TickerResponse) Reset() { - *x = TickerResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TickerResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TickerResponse) ProtoMessage() {} - -func (x *TickerResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[24] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TickerResponse.ProtoReflect.Descriptor instead. +func (m *TickerResponse) Reset() { *m = TickerResponse{} } +func (m *TickerResponse) String() string { return proto.CompactTextString(m) } +func (*TickerResponse) ProtoMessage() {} func (*TickerResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{24} + return fileDescriptor_77a6da22d6a3feb1, []int{22} } -func (x *TickerResponse) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *TickerResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TickerResponse.Unmarshal(m, b) +} +func (m *TickerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TickerResponse.Marshal(b, m, deterministic) +} +func (m *TickerResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_TickerResponse.Merge(m, src) +} +func (m *TickerResponse) XXX_Size() int { + return xxx_messageInfo_TickerResponse.Size(m) +} +func (m *TickerResponse) XXX_DiscardUnknown() { + xxx_messageInfo_TickerResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_TickerResponse proto.InternalMessageInfo + +func (m *TickerResponse) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *TickerResponse) GetLastUpdated() int64 { - if x != nil { - return x.LastUpdated +func (m *TickerResponse) GetLastUpdated() int64 { + if m != nil { + return m.LastUpdated } return 0 } -func (x *TickerResponse) GetCurrencyPair() string { - if x != nil { - return x.CurrencyPair +func (m *TickerResponse) GetCurrencyPair() string { + if m != nil { + return m.CurrencyPair } return "" } -func (x *TickerResponse) GetLast() float64 { - if x != nil { - return x.Last +func (m *TickerResponse) GetLast() float64 { + if m != nil { + return m.Last } return 0 } -func (x *TickerResponse) GetHigh() float64 { - if x != nil { - return x.High +func (m *TickerResponse) GetHigh() float64 { + if m != nil { + return m.High } return 0 } -func (x *TickerResponse) GetLow() float64 { - if x != nil { - return x.Low +func (m *TickerResponse) GetLow() float64 { + if m != nil { + return m.Low } return 0 } -func (x *TickerResponse) GetBid() float64 { - if x != nil { - return x.Bid +func (m *TickerResponse) GetBid() float64 { + if m != nil { + return m.Bid } return 0 } -func (x *TickerResponse) GetAsk() float64 { - if x != nil { - return x.Ask +func (m *TickerResponse) GetAsk() float64 { + if m != nil { + return m.Ask } return 0 } -func (x *TickerResponse) GetVolume() float64 { - if x != nil { - return x.Volume +func (m *TickerResponse) GetVolume() float64 { + if m != nil { + return m.Volume } return 0 } -func (x *TickerResponse) GetPriceAth() float64 { - if x != nil { - return x.PriceAth +func (m *TickerResponse) GetPriceAth() float64 { + if m != nil { + return m.PriceAth } return 0 } type GetTickersRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetTickersRequest) Reset() { - *x = GetTickersRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetTickersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetTickersRequest) ProtoMessage() {} - -func (x *GetTickersRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[25] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetTickersRequest.ProtoReflect.Descriptor instead. +func (m *GetTickersRequest) Reset() { *m = GetTickersRequest{} } +func (m *GetTickersRequest) String() string { return proto.CompactTextString(m) } +func (*GetTickersRequest) ProtoMessage() {} func (*GetTickersRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{25} + return fileDescriptor_77a6da22d6a3feb1, []int{23} } +func (m *GetTickersRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTickersRequest.Unmarshal(m, b) +} +func (m *GetTickersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTickersRequest.Marshal(b, m, deterministic) +} +func (m *GetTickersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTickersRequest.Merge(m, src) +} +func (m *GetTickersRequest) XXX_Size() int { + return xxx_messageInfo_GetTickersRequest.Size(m) +} +func (m *GetTickersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTickersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTickersRequest proto.InternalMessageInfo + type Tickers struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Tickers []*TickerResponse `protobuf:"bytes,2,rep,name=tickers,proto3" json:"tickers,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Tickers []*TickerResponse `protobuf:"bytes,2,rep,name=tickers,proto3" json:"tickers,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *Tickers) Reset() { - *x = Tickers{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Tickers) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Tickers) ProtoMessage() {} - -func (x *Tickers) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[26] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Tickers.ProtoReflect.Descriptor instead. +func (m *Tickers) Reset() { *m = Tickers{} } +func (m *Tickers) String() string { return proto.CompactTextString(m) } +func (*Tickers) ProtoMessage() {} func (*Tickers) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{26} + return fileDescriptor_77a6da22d6a3feb1, []int{24} } -func (x *Tickers) GetExchange() string { - if x != nil { - return x.Exchange +func (m *Tickers) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Tickers.Unmarshal(m, b) +} +func (m *Tickers) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Tickers.Marshal(b, m, deterministic) +} +func (m *Tickers) XXX_Merge(src proto.Message) { + xxx_messageInfo_Tickers.Merge(m, src) +} +func (m *Tickers) XXX_Size() int { + return xxx_messageInfo_Tickers.Size(m) +} +func (m *Tickers) XXX_DiscardUnknown() { + xxx_messageInfo_Tickers.DiscardUnknown(m) +} + +var xxx_messageInfo_Tickers proto.InternalMessageInfo + +func (m *Tickers) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *Tickers) GetTickers() []*TickerResponse { - if x != nil { - return x.Tickers +func (m *Tickers) GetTickers() []*TickerResponse { + if m != nil { + return m.Tickers } return nil } type GetTickersResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Tickers []*Tickers `protobuf:"bytes,1,rep,name=tickers,proto3" json:"tickers,omitempty"` + Tickers []*Tickers `protobuf:"bytes,1,rep,name=tickers,proto3" json:"tickers,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetTickersResponse) Reset() { - *x = GetTickersResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetTickersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetTickersResponse) ProtoMessage() {} - -func (x *GetTickersResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[27] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetTickersResponse.ProtoReflect.Descriptor instead. +func (m *GetTickersResponse) Reset() { *m = GetTickersResponse{} } +func (m *GetTickersResponse) String() string { return proto.CompactTextString(m) } +func (*GetTickersResponse) ProtoMessage() {} func (*GetTickersResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{27} + return fileDescriptor_77a6da22d6a3feb1, []int{25} } -func (x *GetTickersResponse) GetTickers() []*Tickers { - if x != nil { - return x.Tickers +func (m *GetTickersResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTickersResponse.Unmarshal(m, b) +} +func (m *GetTickersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTickersResponse.Marshal(b, m, deterministic) +} +func (m *GetTickersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTickersResponse.Merge(m, src) +} +func (m *GetTickersResponse) XXX_Size() int { + return xxx_messageInfo_GetTickersResponse.Size(m) +} +func (m *GetTickersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetTickersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTickersResponse proto.InternalMessageInfo + +func (m *GetTickersResponse) GetTickers() []*Tickers { + if m != nil { + return m.Tickers } return nil } type GetOrderbookRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetOrderbookRequest) Reset() { - *x = GetOrderbookRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetOrderbookRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetOrderbookRequest) ProtoMessage() {} - -func (x *GetOrderbookRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[28] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetOrderbookRequest.ProtoReflect.Descriptor instead. +func (m *GetOrderbookRequest) Reset() { *m = GetOrderbookRequest{} } +func (m *GetOrderbookRequest) String() string { return proto.CompactTextString(m) } +func (*GetOrderbookRequest) ProtoMessage() {} func (*GetOrderbookRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{28} + return fileDescriptor_77a6da22d6a3feb1, []int{26} } -func (x *GetOrderbookRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetOrderbookRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetOrderbookRequest.Unmarshal(m, b) +} +func (m *GetOrderbookRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetOrderbookRequest.Marshal(b, m, deterministic) +} +func (m *GetOrderbookRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetOrderbookRequest.Merge(m, src) +} +func (m *GetOrderbookRequest) XXX_Size() int { + return xxx_messageInfo_GetOrderbookRequest.Size(m) +} +func (m *GetOrderbookRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetOrderbookRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetOrderbookRequest proto.InternalMessageInfo + +func (m *GetOrderbookRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetOrderbookRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *GetOrderbookRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *GetOrderbookRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *GetOrderbookRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } type OrderbookItem struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Amount float64 `protobuf:"fixed64,1,opt,name=amount,proto3" json:"amount,omitempty"` - Price float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"` - Id int64 `protobuf:"varint,3,opt,name=id,proto3" json:"id,omitempty"` + Amount float64 `protobuf:"fixed64,1,opt,name=amount,proto3" json:"amount,omitempty"` + Price float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"` + Id int64 `protobuf:"varint,3,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OrderbookItem) Reset() { - *x = OrderbookItem{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[29] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OrderbookItem) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OrderbookItem) ProtoMessage() {} - -func (x *OrderbookItem) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[29] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OrderbookItem.ProtoReflect.Descriptor instead. +func (m *OrderbookItem) Reset() { *m = OrderbookItem{} } +func (m *OrderbookItem) String() string { return proto.CompactTextString(m) } +func (*OrderbookItem) ProtoMessage() {} func (*OrderbookItem) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{29} + return fileDescriptor_77a6da22d6a3feb1, []int{27} } -func (x *OrderbookItem) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *OrderbookItem) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OrderbookItem.Unmarshal(m, b) +} +func (m *OrderbookItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OrderbookItem.Marshal(b, m, deterministic) +} +func (m *OrderbookItem) XXX_Merge(src proto.Message) { + xxx_messageInfo_OrderbookItem.Merge(m, src) +} +func (m *OrderbookItem) XXX_Size() int { + return xxx_messageInfo_OrderbookItem.Size(m) +} +func (m *OrderbookItem) XXX_DiscardUnknown() { + xxx_messageInfo_OrderbookItem.DiscardUnknown(m) +} + +var xxx_messageInfo_OrderbookItem proto.InternalMessageInfo + +func (m *OrderbookItem) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *OrderbookItem) GetPrice() float64 { - if x != nil { - return x.Price +func (m *OrderbookItem) GetPrice() float64 { + if m != nil { + return m.Price } return 0 } -func (x *OrderbookItem) GetId() int64 { - if x != nil { - return x.Id +func (m *OrderbookItem) GetId() int64 { + if m != nil { + return m.Id } return 0 } type OrderbookResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Pair *CurrencyPair `protobuf:"bytes,1,opt,name=pair,proto3" json:"pair,omitempty"` - CurrencyPair string `protobuf:"bytes,2,opt,name=currency_pair,json=currencyPair,proto3" json:"currency_pair,omitempty"` - Bids []*OrderbookItem `protobuf:"bytes,3,rep,name=bids,proto3" json:"bids,omitempty"` - Asks []*OrderbookItem `protobuf:"bytes,4,rep,name=asks,proto3" json:"asks,omitempty"` - LastUpdated int64 `protobuf:"varint,5,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"` - AssetType string `protobuf:"bytes,6,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,1,opt,name=pair,proto3" json:"pair,omitempty"` + CurrencyPair string `protobuf:"bytes,2,opt,name=currency_pair,json=currencyPair,proto3" json:"currency_pair,omitempty"` + Bids []*OrderbookItem `protobuf:"bytes,3,rep,name=bids,proto3" json:"bids,omitempty"` + Asks []*OrderbookItem `protobuf:"bytes,4,rep,name=asks,proto3" json:"asks,omitempty"` + LastUpdated int64 `protobuf:"varint,5,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"` + AssetType string `protobuf:"bytes,6,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OrderbookResponse) Reset() { - *x = OrderbookResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[30] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OrderbookResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OrderbookResponse) ProtoMessage() {} - -func (x *OrderbookResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[30] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OrderbookResponse.ProtoReflect.Descriptor instead. +func (m *OrderbookResponse) Reset() { *m = OrderbookResponse{} } +func (m *OrderbookResponse) String() string { return proto.CompactTextString(m) } +func (*OrderbookResponse) ProtoMessage() {} func (*OrderbookResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{30} + return fileDescriptor_77a6da22d6a3feb1, []int{28} } -func (x *OrderbookResponse) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *OrderbookResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OrderbookResponse.Unmarshal(m, b) +} +func (m *OrderbookResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OrderbookResponse.Marshal(b, m, deterministic) +} +func (m *OrderbookResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_OrderbookResponse.Merge(m, src) +} +func (m *OrderbookResponse) XXX_Size() int { + return xxx_messageInfo_OrderbookResponse.Size(m) +} +func (m *OrderbookResponse) XXX_DiscardUnknown() { + xxx_messageInfo_OrderbookResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_OrderbookResponse proto.InternalMessageInfo + +func (m *OrderbookResponse) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *OrderbookResponse) GetCurrencyPair() string { - if x != nil { - return x.CurrencyPair +func (m *OrderbookResponse) GetCurrencyPair() string { + if m != nil { + return m.CurrencyPair } return "" } -func (x *OrderbookResponse) GetBids() []*OrderbookItem { - if x != nil { - return x.Bids +func (m *OrderbookResponse) GetBids() []*OrderbookItem { + if m != nil { + return m.Bids } return nil } -func (x *OrderbookResponse) GetAsks() []*OrderbookItem { - if x != nil { - return x.Asks +func (m *OrderbookResponse) GetAsks() []*OrderbookItem { + if m != nil { + return m.Asks } return nil } -func (x *OrderbookResponse) GetLastUpdated() int64 { - if x != nil { - return x.LastUpdated +func (m *OrderbookResponse) GetLastUpdated() int64 { + if m != nil { + return m.LastUpdated } return 0 } -func (x *OrderbookResponse) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *OrderbookResponse) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } type GetOrderbooksRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetOrderbooksRequest) Reset() { - *x = GetOrderbooksRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[31] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetOrderbooksRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetOrderbooksRequest) ProtoMessage() {} - -func (x *GetOrderbooksRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[31] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetOrderbooksRequest.ProtoReflect.Descriptor instead. +func (m *GetOrderbooksRequest) Reset() { *m = GetOrderbooksRequest{} } +func (m *GetOrderbooksRequest) String() string { return proto.CompactTextString(m) } +func (*GetOrderbooksRequest) ProtoMessage() {} func (*GetOrderbooksRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{31} + return fileDescriptor_77a6da22d6a3feb1, []int{29} } +func (m *GetOrderbooksRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetOrderbooksRequest.Unmarshal(m, b) +} +func (m *GetOrderbooksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetOrderbooksRequest.Marshal(b, m, deterministic) +} +func (m *GetOrderbooksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetOrderbooksRequest.Merge(m, src) +} +func (m *GetOrderbooksRequest) XXX_Size() int { + return xxx_messageInfo_GetOrderbooksRequest.Size(m) +} +func (m *GetOrderbooksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetOrderbooksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetOrderbooksRequest proto.InternalMessageInfo + type Orderbooks struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Orderbooks []*OrderbookResponse `protobuf:"bytes,2,rep,name=orderbooks,proto3" json:"orderbooks,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Orderbooks []*OrderbookResponse `protobuf:"bytes,2,rep,name=orderbooks,proto3" json:"orderbooks,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *Orderbooks) Reset() { - *x = Orderbooks{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[32] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Orderbooks) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Orderbooks) ProtoMessage() {} - -func (x *Orderbooks) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[32] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Orderbooks.ProtoReflect.Descriptor instead. +func (m *Orderbooks) Reset() { *m = Orderbooks{} } +func (m *Orderbooks) String() string { return proto.CompactTextString(m) } +func (*Orderbooks) ProtoMessage() {} func (*Orderbooks) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{32} + return fileDescriptor_77a6da22d6a3feb1, []int{30} } -func (x *Orderbooks) GetExchange() string { - if x != nil { - return x.Exchange +func (m *Orderbooks) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Orderbooks.Unmarshal(m, b) +} +func (m *Orderbooks) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Orderbooks.Marshal(b, m, deterministic) +} +func (m *Orderbooks) XXX_Merge(src proto.Message) { + xxx_messageInfo_Orderbooks.Merge(m, src) +} +func (m *Orderbooks) XXX_Size() int { + return xxx_messageInfo_Orderbooks.Size(m) +} +func (m *Orderbooks) XXX_DiscardUnknown() { + xxx_messageInfo_Orderbooks.DiscardUnknown(m) +} + +var xxx_messageInfo_Orderbooks proto.InternalMessageInfo + +func (m *Orderbooks) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *Orderbooks) GetOrderbooks() []*OrderbookResponse { - if x != nil { - return x.Orderbooks +func (m *Orderbooks) GetOrderbooks() []*OrderbookResponse { + if m != nil { + return m.Orderbooks } return nil } type GetOrderbooksResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Orderbooks []*Orderbooks `protobuf:"bytes,1,rep,name=orderbooks,proto3" json:"orderbooks,omitempty"` + Orderbooks []*Orderbooks `protobuf:"bytes,1,rep,name=orderbooks,proto3" json:"orderbooks,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetOrderbooksResponse) Reset() { - *x = GetOrderbooksResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[33] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetOrderbooksResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetOrderbooksResponse) ProtoMessage() {} - -func (x *GetOrderbooksResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[33] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetOrderbooksResponse.ProtoReflect.Descriptor instead. +func (m *GetOrderbooksResponse) Reset() { *m = GetOrderbooksResponse{} } +func (m *GetOrderbooksResponse) String() string { return proto.CompactTextString(m) } +func (*GetOrderbooksResponse) ProtoMessage() {} func (*GetOrderbooksResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{33} + return fileDescriptor_77a6da22d6a3feb1, []int{31} } -func (x *GetOrderbooksResponse) GetOrderbooks() []*Orderbooks { - if x != nil { - return x.Orderbooks +func (m *GetOrderbooksResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetOrderbooksResponse.Unmarshal(m, b) +} +func (m *GetOrderbooksResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetOrderbooksResponse.Marshal(b, m, deterministic) +} +func (m *GetOrderbooksResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetOrderbooksResponse.Merge(m, src) +} +func (m *GetOrderbooksResponse) XXX_Size() int { + return xxx_messageInfo_GetOrderbooksResponse.Size(m) +} +func (m *GetOrderbooksResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetOrderbooksResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetOrderbooksResponse proto.InternalMessageInfo + +func (m *GetOrderbooksResponse) GetOrderbooks() []*Orderbooks { + if m != nil { + return m.Orderbooks } return nil } type GetAccountInfoRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetAccountInfoRequest) Reset() { - *x = GetAccountInfoRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[34] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetAccountInfoRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetAccountInfoRequest) ProtoMessage() {} - -func (x *GetAccountInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[34] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetAccountInfoRequest.ProtoReflect.Descriptor instead. +func (m *GetAccountInfoRequest) Reset() { *m = GetAccountInfoRequest{} } +func (m *GetAccountInfoRequest) String() string { return proto.CompactTextString(m) } +func (*GetAccountInfoRequest) ProtoMessage() {} func (*GetAccountInfoRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{34} + return fileDescriptor_77a6da22d6a3feb1, []int{32} } -func (x *GetAccountInfoRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetAccountInfoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetAccountInfoRequest.Unmarshal(m, b) +} +func (m *GetAccountInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetAccountInfoRequest.Marshal(b, m, deterministic) +} +func (m *GetAccountInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetAccountInfoRequest.Merge(m, src) +} +func (m *GetAccountInfoRequest) XXX_Size() int { + return xxx_messageInfo_GetAccountInfoRequest.Size(m) +} +func (m *GetAccountInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetAccountInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetAccountInfoRequest proto.InternalMessageInfo + +func (m *GetAccountInfoRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } type Account struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Currencies []*AccountCurrencyInfo `protobuf:"bytes,2,rep,name=currencies,proto3" json:"currencies,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Currencies []*AccountCurrencyInfo `protobuf:"bytes,2,rep,name=currencies,proto3" json:"currencies,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *Account) Reset() { - *x = Account{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[35] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Account) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Account) ProtoMessage() {} - -func (x *Account) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[35] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Account.ProtoReflect.Descriptor instead. +func (m *Account) Reset() { *m = Account{} } +func (m *Account) String() string { return proto.CompactTextString(m) } +func (*Account) ProtoMessage() {} func (*Account) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{35} + return fileDescriptor_77a6da22d6a3feb1, []int{33} } -func (x *Account) GetId() string { - if x != nil { - return x.Id +func (m *Account) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Account.Unmarshal(m, b) +} +func (m *Account) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Account.Marshal(b, m, deterministic) +} +func (m *Account) XXX_Merge(src proto.Message) { + xxx_messageInfo_Account.Merge(m, src) +} +func (m *Account) XXX_Size() int { + return xxx_messageInfo_Account.Size(m) +} +func (m *Account) XXX_DiscardUnknown() { + xxx_messageInfo_Account.DiscardUnknown(m) +} + +var xxx_messageInfo_Account proto.InternalMessageInfo + +func (m *Account) GetId() string { + if m != nil { + return m.Id } return "" } -func (x *Account) GetCurrencies() []*AccountCurrencyInfo { - if x != nil { - return x.Currencies +func (m *Account) GetCurrencies() []*AccountCurrencyInfo { + if m != nil { + return m.Currencies } return nil } type AccountCurrencyInfo struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` - TotalValue float64 `protobuf:"fixed64,2,opt,name=total_value,json=totalValue,proto3" json:"total_value,omitempty"` - Hold float64 `protobuf:"fixed64,3,opt,name=hold,proto3" json:"hold,omitempty"` + Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` + TotalValue float64 `protobuf:"fixed64,2,opt,name=total_value,json=totalValue,proto3" json:"total_value,omitempty"` + Hold float64 `protobuf:"fixed64,3,opt,name=hold,proto3" json:"hold,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *AccountCurrencyInfo) Reset() { - *x = AccountCurrencyInfo{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[36] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AccountCurrencyInfo) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AccountCurrencyInfo) ProtoMessage() {} - -func (x *AccountCurrencyInfo) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[36] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AccountCurrencyInfo.ProtoReflect.Descriptor instead. +func (m *AccountCurrencyInfo) Reset() { *m = AccountCurrencyInfo{} } +func (m *AccountCurrencyInfo) String() string { return proto.CompactTextString(m) } +func (*AccountCurrencyInfo) ProtoMessage() {} func (*AccountCurrencyInfo) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{36} + return fileDescriptor_77a6da22d6a3feb1, []int{34} } -func (x *AccountCurrencyInfo) GetCurrency() string { - if x != nil { - return x.Currency +func (m *AccountCurrencyInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AccountCurrencyInfo.Unmarshal(m, b) +} +func (m *AccountCurrencyInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AccountCurrencyInfo.Marshal(b, m, deterministic) +} +func (m *AccountCurrencyInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_AccountCurrencyInfo.Merge(m, src) +} +func (m *AccountCurrencyInfo) XXX_Size() int { + return xxx_messageInfo_AccountCurrencyInfo.Size(m) +} +func (m *AccountCurrencyInfo) XXX_DiscardUnknown() { + xxx_messageInfo_AccountCurrencyInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_AccountCurrencyInfo proto.InternalMessageInfo + +func (m *AccountCurrencyInfo) GetCurrency() string { + if m != nil { + return m.Currency } return "" } -func (x *AccountCurrencyInfo) GetTotalValue() float64 { - if x != nil { - return x.TotalValue +func (m *AccountCurrencyInfo) GetTotalValue() float64 { + if m != nil { + return m.TotalValue } return 0 } -func (x *AccountCurrencyInfo) GetHold() float64 { - if x != nil { - return x.Hold +func (m *AccountCurrencyInfo) GetHold() float64 { + if m != nil { + return m.Hold } return 0 } type GetAccountInfoResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Accounts []*Account `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Accounts []*Account `protobuf:"bytes,2,rep,name=accounts,proto3" json:"accounts,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetAccountInfoResponse) Reset() { - *x = GetAccountInfoResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[37] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetAccountInfoResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetAccountInfoResponse) ProtoMessage() {} - -func (x *GetAccountInfoResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[37] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetAccountInfoResponse.ProtoReflect.Descriptor instead. +func (m *GetAccountInfoResponse) Reset() { *m = GetAccountInfoResponse{} } +func (m *GetAccountInfoResponse) String() string { return proto.CompactTextString(m) } +func (*GetAccountInfoResponse) ProtoMessage() {} func (*GetAccountInfoResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{37} + return fileDescriptor_77a6da22d6a3feb1, []int{35} } -func (x *GetAccountInfoResponse) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetAccountInfoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetAccountInfoResponse.Unmarshal(m, b) +} +func (m *GetAccountInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetAccountInfoResponse.Marshal(b, m, deterministic) +} +func (m *GetAccountInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetAccountInfoResponse.Merge(m, src) +} +func (m *GetAccountInfoResponse) XXX_Size() int { + return xxx_messageInfo_GetAccountInfoResponse.Size(m) +} +func (m *GetAccountInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetAccountInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetAccountInfoResponse proto.InternalMessageInfo + +func (m *GetAccountInfoResponse) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetAccountInfoResponse) GetAccounts() []*Account { - if x != nil { - return x.Accounts +func (m *GetAccountInfoResponse) GetAccounts() []*Account { + if m != nil { + return m.Accounts } return nil } type GetConfigRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetConfigRequest) Reset() { - *x = GetConfigRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[38] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetConfigRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetConfigRequest) ProtoMessage() {} - -func (x *GetConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[38] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetConfigRequest.ProtoReflect.Descriptor instead. +func (m *GetConfigRequest) Reset() { *m = GetConfigRequest{} } +func (m *GetConfigRequest) String() string { return proto.CompactTextString(m) } +func (*GetConfigRequest) ProtoMessage() {} func (*GetConfigRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{38} + return fileDescriptor_77a6da22d6a3feb1, []int{36} } +func (m *GetConfigRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetConfigRequest.Unmarshal(m, b) +} +func (m *GetConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetConfigRequest.Marshal(b, m, deterministic) +} +func (m *GetConfigRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetConfigRequest.Merge(m, src) +} +func (m *GetConfigRequest) XXX_Size() int { + return xxx_messageInfo_GetConfigRequest.Size(m) +} +func (m *GetConfigRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetConfigRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetConfigRequest proto.InternalMessageInfo + type GetConfigResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetConfigResponse) Reset() { - *x = GetConfigResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[39] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetConfigResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetConfigResponse) ProtoMessage() {} - -func (x *GetConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[39] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetConfigResponse.ProtoReflect.Descriptor instead. +func (m *GetConfigResponse) Reset() { *m = GetConfigResponse{} } +func (m *GetConfigResponse) String() string { return proto.CompactTextString(m) } +func (*GetConfigResponse) ProtoMessage() {} func (*GetConfigResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{39} + return fileDescriptor_77a6da22d6a3feb1, []int{37} } -func (x *GetConfigResponse) GetData() []byte { - if x != nil { - return x.Data +func (m *GetConfigResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetConfigResponse.Unmarshal(m, b) +} +func (m *GetConfigResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetConfigResponse.Marshal(b, m, deterministic) +} +func (m *GetConfigResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetConfigResponse.Merge(m, src) +} +func (m *GetConfigResponse) XXX_Size() int { + return xxx_messageInfo_GetConfigResponse.Size(m) +} +func (m *GetConfigResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetConfigResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetConfigResponse proto.InternalMessageInfo + +func (m *GetConfigResponse) GetData() []byte { + if m != nil { + return m.Data } return nil } type PortfolioAddress struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - CoinType string `protobuf:"bytes,2,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Balance float64 `protobuf:"fixed64,4,opt,name=balance,proto3" json:"balance,omitempty"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + CoinType string `protobuf:"bytes,2,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Balance float64 `protobuf:"fixed64,4,opt,name=balance,proto3" json:"balance,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *PortfolioAddress) Reset() { - *x = PortfolioAddress{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[40] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PortfolioAddress) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PortfolioAddress) ProtoMessage() {} - -func (x *PortfolioAddress) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[40] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PortfolioAddress.ProtoReflect.Descriptor instead. +func (m *PortfolioAddress) Reset() { *m = PortfolioAddress{} } +func (m *PortfolioAddress) String() string { return proto.CompactTextString(m) } +func (*PortfolioAddress) ProtoMessage() {} func (*PortfolioAddress) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{40} + return fileDescriptor_77a6da22d6a3feb1, []int{38} } -func (x *PortfolioAddress) GetAddress() string { - if x != nil { - return x.Address +func (m *PortfolioAddress) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PortfolioAddress.Unmarshal(m, b) +} +func (m *PortfolioAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PortfolioAddress.Marshal(b, m, deterministic) +} +func (m *PortfolioAddress) XXX_Merge(src proto.Message) { + xxx_messageInfo_PortfolioAddress.Merge(m, src) +} +func (m *PortfolioAddress) XXX_Size() int { + return xxx_messageInfo_PortfolioAddress.Size(m) +} +func (m *PortfolioAddress) XXX_DiscardUnknown() { + xxx_messageInfo_PortfolioAddress.DiscardUnknown(m) +} + +var xxx_messageInfo_PortfolioAddress proto.InternalMessageInfo + +func (m *PortfolioAddress) GetAddress() string { + if m != nil { + return m.Address } return "" } -func (x *PortfolioAddress) GetCoinType() string { - if x != nil { - return x.CoinType +func (m *PortfolioAddress) GetCoinType() string { + if m != nil { + return m.CoinType } return "" } -func (x *PortfolioAddress) GetDescription() string { - if x != nil { - return x.Description +func (m *PortfolioAddress) GetDescription() string { + if m != nil { + return m.Description } return "" } -func (x *PortfolioAddress) GetBalance() float64 { - if x != nil { - return x.Balance +func (m *PortfolioAddress) GetBalance() float64 { + if m != nil { + return m.Balance } return 0 } type GetPortfolioRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetPortfolioRequest) Reset() { - *x = GetPortfolioRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[41] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetPortfolioRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetPortfolioRequest) ProtoMessage() {} - -func (x *GetPortfolioRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[41] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetPortfolioRequest.ProtoReflect.Descriptor instead. +func (m *GetPortfolioRequest) Reset() { *m = GetPortfolioRequest{} } +func (m *GetPortfolioRequest) String() string { return proto.CompactTextString(m) } +func (*GetPortfolioRequest) ProtoMessage() {} func (*GetPortfolioRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{41} + return fileDescriptor_77a6da22d6a3feb1, []int{39} } +func (m *GetPortfolioRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetPortfolioRequest.Unmarshal(m, b) +} +func (m *GetPortfolioRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetPortfolioRequest.Marshal(b, m, deterministic) +} +func (m *GetPortfolioRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetPortfolioRequest.Merge(m, src) +} +func (m *GetPortfolioRequest) XXX_Size() int { + return xxx_messageInfo_GetPortfolioRequest.Size(m) +} +func (m *GetPortfolioRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetPortfolioRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetPortfolioRequest proto.InternalMessageInfo + type GetPortfolioResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Portfolio []*PortfolioAddress `protobuf:"bytes,1,rep,name=portfolio,proto3" json:"portfolio,omitempty"` + Portfolio []*PortfolioAddress `protobuf:"bytes,1,rep,name=portfolio,proto3" json:"portfolio,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetPortfolioResponse) Reset() { - *x = GetPortfolioResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[42] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetPortfolioResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetPortfolioResponse) ProtoMessage() {} - -func (x *GetPortfolioResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[42] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetPortfolioResponse.ProtoReflect.Descriptor instead. +func (m *GetPortfolioResponse) Reset() { *m = GetPortfolioResponse{} } +func (m *GetPortfolioResponse) String() string { return proto.CompactTextString(m) } +func (*GetPortfolioResponse) ProtoMessage() {} func (*GetPortfolioResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{42} + return fileDescriptor_77a6da22d6a3feb1, []int{40} } -func (x *GetPortfolioResponse) GetPortfolio() []*PortfolioAddress { - if x != nil { - return x.Portfolio +func (m *GetPortfolioResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetPortfolioResponse.Unmarshal(m, b) +} +func (m *GetPortfolioResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetPortfolioResponse.Marshal(b, m, deterministic) +} +func (m *GetPortfolioResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetPortfolioResponse.Merge(m, src) +} +func (m *GetPortfolioResponse) XXX_Size() int { + return xxx_messageInfo_GetPortfolioResponse.Size(m) +} +func (m *GetPortfolioResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetPortfolioResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetPortfolioResponse proto.InternalMessageInfo + +func (m *GetPortfolioResponse) GetPortfolio() []*PortfolioAddress { + if m != nil { + return m.Portfolio } return nil } type GetPortfolioSummaryRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetPortfolioSummaryRequest) Reset() { - *x = GetPortfolioSummaryRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[43] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetPortfolioSummaryRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetPortfolioSummaryRequest) ProtoMessage() {} - -func (x *GetPortfolioSummaryRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[43] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetPortfolioSummaryRequest.ProtoReflect.Descriptor instead. +func (m *GetPortfolioSummaryRequest) Reset() { *m = GetPortfolioSummaryRequest{} } +func (m *GetPortfolioSummaryRequest) String() string { return proto.CompactTextString(m) } +func (*GetPortfolioSummaryRequest) ProtoMessage() {} func (*GetPortfolioSummaryRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{43} + return fileDescriptor_77a6da22d6a3feb1, []int{41} } +func (m *GetPortfolioSummaryRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetPortfolioSummaryRequest.Unmarshal(m, b) +} +func (m *GetPortfolioSummaryRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetPortfolioSummaryRequest.Marshal(b, m, deterministic) +} +func (m *GetPortfolioSummaryRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetPortfolioSummaryRequest.Merge(m, src) +} +func (m *GetPortfolioSummaryRequest) XXX_Size() int { + return xxx_messageInfo_GetPortfolioSummaryRequest.Size(m) +} +func (m *GetPortfolioSummaryRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetPortfolioSummaryRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetPortfolioSummaryRequest proto.InternalMessageInfo + type Coin struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Coin string `protobuf:"bytes,1,opt,name=coin,proto3" json:"coin,omitempty"` - Balance float64 `protobuf:"fixed64,2,opt,name=balance,proto3" json:"balance,omitempty"` - Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` - Percentage float64 `protobuf:"fixed64,4,opt,name=percentage,proto3" json:"percentage,omitempty"` + Coin string `protobuf:"bytes,1,opt,name=coin,proto3" json:"coin,omitempty"` + Balance float64 `protobuf:"fixed64,2,opt,name=balance,proto3" json:"balance,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + Percentage float64 `protobuf:"fixed64,4,opt,name=percentage,proto3" json:"percentage,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *Coin) Reset() { - *x = Coin{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[44] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Coin) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Coin) ProtoMessage() {} - -func (x *Coin) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[44] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Coin.ProtoReflect.Descriptor instead. +func (m *Coin) Reset() { *m = Coin{} } +func (m *Coin) String() string { return proto.CompactTextString(m) } +func (*Coin) ProtoMessage() {} func (*Coin) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{44} + return fileDescriptor_77a6da22d6a3feb1, []int{42} } -func (x *Coin) GetCoin() string { - if x != nil { - return x.Coin +func (m *Coin) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Coin.Unmarshal(m, b) +} +func (m *Coin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Coin.Marshal(b, m, deterministic) +} +func (m *Coin) XXX_Merge(src proto.Message) { + xxx_messageInfo_Coin.Merge(m, src) +} +func (m *Coin) XXX_Size() int { + return xxx_messageInfo_Coin.Size(m) +} +func (m *Coin) XXX_DiscardUnknown() { + xxx_messageInfo_Coin.DiscardUnknown(m) +} + +var xxx_messageInfo_Coin proto.InternalMessageInfo + +func (m *Coin) GetCoin() string { + if m != nil { + return m.Coin } return "" } -func (x *Coin) GetBalance() float64 { - if x != nil { - return x.Balance +func (m *Coin) GetBalance() float64 { + if m != nil { + return m.Balance } return 0 } -func (x *Coin) GetAddress() string { - if x != nil { - return x.Address +func (m *Coin) GetAddress() string { + if m != nil { + return m.Address } return "" } -func (x *Coin) GetPercentage() float64 { - if x != nil { - return x.Percentage +func (m *Coin) GetPercentage() float64 { + if m != nil { + return m.Percentage } return 0 } type OfflineCoinSummary struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - Balance float64 `protobuf:"fixed64,2,opt,name=balance,proto3" json:"balance,omitempty"` - Percentage float64 `protobuf:"fixed64,3,opt,name=percentage,proto3" json:"percentage,omitempty"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Balance float64 `protobuf:"fixed64,2,opt,name=balance,proto3" json:"balance,omitempty"` + Percentage float64 `protobuf:"fixed64,3,opt,name=percentage,proto3" json:"percentage,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OfflineCoinSummary) Reset() { - *x = OfflineCoinSummary{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[45] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OfflineCoinSummary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OfflineCoinSummary) ProtoMessage() {} - -func (x *OfflineCoinSummary) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[45] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OfflineCoinSummary.ProtoReflect.Descriptor instead. +func (m *OfflineCoinSummary) Reset() { *m = OfflineCoinSummary{} } +func (m *OfflineCoinSummary) String() string { return proto.CompactTextString(m) } +func (*OfflineCoinSummary) ProtoMessage() {} func (*OfflineCoinSummary) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{45} + return fileDescriptor_77a6da22d6a3feb1, []int{43} } -func (x *OfflineCoinSummary) GetAddress() string { - if x != nil { - return x.Address +func (m *OfflineCoinSummary) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OfflineCoinSummary.Unmarshal(m, b) +} +func (m *OfflineCoinSummary) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OfflineCoinSummary.Marshal(b, m, deterministic) +} +func (m *OfflineCoinSummary) XXX_Merge(src proto.Message) { + xxx_messageInfo_OfflineCoinSummary.Merge(m, src) +} +func (m *OfflineCoinSummary) XXX_Size() int { + return xxx_messageInfo_OfflineCoinSummary.Size(m) +} +func (m *OfflineCoinSummary) XXX_DiscardUnknown() { + xxx_messageInfo_OfflineCoinSummary.DiscardUnknown(m) +} + +var xxx_messageInfo_OfflineCoinSummary proto.InternalMessageInfo + +func (m *OfflineCoinSummary) GetAddress() string { + if m != nil { + return m.Address } return "" } -func (x *OfflineCoinSummary) GetBalance() float64 { - if x != nil { - return x.Balance +func (m *OfflineCoinSummary) GetBalance() float64 { + if m != nil { + return m.Balance } return 0 } -func (x *OfflineCoinSummary) GetPercentage() float64 { - if x != nil { - return x.Percentage +func (m *OfflineCoinSummary) GetPercentage() float64 { + if m != nil { + return m.Percentage } return 0 } type OnlineCoinSummary struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Balance float64 `protobuf:"fixed64,1,opt,name=balance,proto3" json:"balance,omitempty"` - Percentage float64 `protobuf:"fixed64,2,opt,name=percentage,proto3" json:"percentage,omitempty"` + Balance float64 `protobuf:"fixed64,1,opt,name=balance,proto3" json:"balance,omitempty"` + Percentage float64 `protobuf:"fixed64,2,opt,name=percentage,proto3" json:"percentage,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OnlineCoinSummary) Reset() { - *x = OnlineCoinSummary{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[46] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OnlineCoinSummary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OnlineCoinSummary) ProtoMessage() {} - -func (x *OnlineCoinSummary) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[46] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OnlineCoinSummary.ProtoReflect.Descriptor instead. +func (m *OnlineCoinSummary) Reset() { *m = OnlineCoinSummary{} } +func (m *OnlineCoinSummary) String() string { return proto.CompactTextString(m) } +func (*OnlineCoinSummary) ProtoMessage() {} func (*OnlineCoinSummary) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{46} + return fileDescriptor_77a6da22d6a3feb1, []int{44} } -func (x *OnlineCoinSummary) GetBalance() float64 { - if x != nil { - return x.Balance +func (m *OnlineCoinSummary) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OnlineCoinSummary.Unmarshal(m, b) +} +func (m *OnlineCoinSummary) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OnlineCoinSummary.Marshal(b, m, deterministic) +} +func (m *OnlineCoinSummary) XXX_Merge(src proto.Message) { + xxx_messageInfo_OnlineCoinSummary.Merge(m, src) +} +func (m *OnlineCoinSummary) XXX_Size() int { + return xxx_messageInfo_OnlineCoinSummary.Size(m) +} +func (m *OnlineCoinSummary) XXX_DiscardUnknown() { + xxx_messageInfo_OnlineCoinSummary.DiscardUnknown(m) +} + +var xxx_messageInfo_OnlineCoinSummary proto.InternalMessageInfo + +func (m *OnlineCoinSummary) GetBalance() float64 { + if m != nil { + return m.Balance } return 0 } -func (x *OnlineCoinSummary) GetPercentage() float64 { - if x != nil { - return x.Percentage +func (m *OnlineCoinSummary) GetPercentage() float64 { + if m != nil { + return m.Percentage } return 0 } type OfflineCoins struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Addresses []*OfflineCoinSummary `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` + Addresses []*OfflineCoinSummary `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OfflineCoins) Reset() { - *x = OfflineCoins{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[47] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OfflineCoins) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OfflineCoins) ProtoMessage() {} - -func (x *OfflineCoins) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[47] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OfflineCoins.ProtoReflect.Descriptor instead. +func (m *OfflineCoins) Reset() { *m = OfflineCoins{} } +func (m *OfflineCoins) String() string { return proto.CompactTextString(m) } +func (*OfflineCoins) ProtoMessage() {} func (*OfflineCoins) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{47} + return fileDescriptor_77a6da22d6a3feb1, []int{45} } -func (x *OfflineCoins) GetAddresses() []*OfflineCoinSummary { - if x != nil { - return x.Addresses +func (m *OfflineCoins) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OfflineCoins.Unmarshal(m, b) +} +func (m *OfflineCoins) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OfflineCoins.Marshal(b, m, deterministic) +} +func (m *OfflineCoins) XXX_Merge(src proto.Message) { + xxx_messageInfo_OfflineCoins.Merge(m, src) +} +func (m *OfflineCoins) XXX_Size() int { + return xxx_messageInfo_OfflineCoins.Size(m) +} +func (m *OfflineCoins) XXX_DiscardUnknown() { + xxx_messageInfo_OfflineCoins.DiscardUnknown(m) +} + +var xxx_messageInfo_OfflineCoins proto.InternalMessageInfo + +func (m *OfflineCoins) GetAddresses() []*OfflineCoinSummary { + if m != nil { + return m.Addresses } return nil } type OnlineCoins struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Coins map[string]*OnlineCoinSummary `protobuf:"bytes,1,rep,name=coins,proto3" json:"coins,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Coins map[string]*OnlineCoinSummary `protobuf:"bytes,1,rep,name=coins,proto3" json:"coins,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OnlineCoins) Reset() { - *x = OnlineCoins{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[48] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OnlineCoins) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OnlineCoins) ProtoMessage() {} - -func (x *OnlineCoins) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[48] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OnlineCoins.ProtoReflect.Descriptor instead. +func (m *OnlineCoins) Reset() { *m = OnlineCoins{} } +func (m *OnlineCoins) String() string { return proto.CompactTextString(m) } +func (*OnlineCoins) ProtoMessage() {} func (*OnlineCoins) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{48} + return fileDescriptor_77a6da22d6a3feb1, []int{46} } -func (x *OnlineCoins) GetCoins() map[string]*OnlineCoinSummary { - if x != nil { - return x.Coins +func (m *OnlineCoins) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OnlineCoins.Unmarshal(m, b) +} +func (m *OnlineCoins) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OnlineCoins.Marshal(b, m, deterministic) +} +func (m *OnlineCoins) XXX_Merge(src proto.Message) { + xxx_messageInfo_OnlineCoins.Merge(m, src) +} +func (m *OnlineCoins) XXX_Size() int { + return xxx_messageInfo_OnlineCoins.Size(m) +} +func (m *OnlineCoins) XXX_DiscardUnknown() { + xxx_messageInfo_OnlineCoins.DiscardUnknown(m) +} + +var xxx_messageInfo_OnlineCoins proto.InternalMessageInfo + +func (m *OnlineCoins) GetCoins() map[string]*OnlineCoinSummary { + if m != nil { + return m.Coins } return nil } type GetPortfolioSummaryResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - CoinTotals []*Coin `protobuf:"bytes,1,rep,name=coin_totals,json=coinTotals,proto3" json:"coin_totals,omitempty"` - CoinsOffline []*Coin `protobuf:"bytes,2,rep,name=coins_offline,json=coinsOffline,proto3" json:"coins_offline,omitempty"` - CoinsOfflineSummary map[string]*OfflineCoins `protobuf:"bytes,3,rep,name=coins_offline_summary,json=coinsOfflineSummary,proto3" json:"coins_offline_summary,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - CoinsOnline []*Coin `protobuf:"bytes,4,rep,name=coins_online,json=coinsOnline,proto3" json:"coins_online,omitempty"` - CoinsOnlineSummary map[string]*OnlineCoins `protobuf:"bytes,5,rep,name=coins_online_summary,json=coinsOnlineSummary,proto3" json:"coins_online_summary,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + CoinTotals []*Coin `protobuf:"bytes,1,rep,name=coin_totals,json=coinTotals,proto3" json:"coin_totals,omitempty"` + CoinsOffline []*Coin `protobuf:"bytes,2,rep,name=coins_offline,json=coinsOffline,proto3" json:"coins_offline,omitempty"` + CoinsOfflineSummary map[string]*OfflineCoins `protobuf:"bytes,3,rep,name=coins_offline_summary,json=coinsOfflineSummary,proto3" json:"coins_offline_summary,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + CoinsOnline []*Coin `protobuf:"bytes,4,rep,name=coins_online,json=coinsOnline,proto3" json:"coins_online,omitempty"` + CoinsOnlineSummary map[string]*OnlineCoins `protobuf:"bytes,5,rep,name=coins_online_summary,json=coinsOnlineSummary,proto3" json:"coins_online_summary,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetPortfolioSummaryResponse) Reset() { - *x = GetPortfolioSummaryResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[49] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetPortfolioSummaryResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetPortfolioSummaryResponse) ProtoMessage() {} - -func (x *GetPortfolioSummaryResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[49] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetPortfolioSummaryResponse.ProtoReflect.Descriptor instead. +func (m *GetPortfolioSummaryResponse) Reset() { *m = GetPortfolioSummaryResponse{} } +func (m *GetPortfolioSummaryResponse) String() string { return proto.CompactTextString(m) } +func (*GetPortfolioSummaryResponse) ProtoMessage() {} func (*GetPortfolioSummaryResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{49} + return fileDescriptor_77a6da22d6a3feb1, []int{47} } -func (x *GetPortfolioSummaryResponse) GetCoinTotals() []*Coin { - if x != nil { - return x.CoinTotals +func (m *GetPortfolioSummaryResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetPortfolioSummaryResponse.Unmarshal(m, b) +} +func (m *GetPortfolioSummaryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetPortfolioSummaryResponse.Marshal(b, m, deterministic) +} +func (m *GetPortfolioSummaryResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetPortfolioSummaryResponse.Merge(m, src) +} +func (m *GetPortfolioSummaryResponse) XXX_Size() int { + return xxx_messageInfo_GetPortfolioSummaryResponse.Size(m) +} +func (m *GetPortfolioSummaryResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetPortfolioSummaryResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetPortfolioSummaryResponse proto.InternalMessageInfo + +func (m *GetPortfolioSummaryResponse) GetCoinTotals() []*Coin { + if m != nil { + return m.CoinTotals } return nil } -func (x *GetPortfolioSummaryResponse) GetCoinsOffline() []*Coin { - if x != nil { - return x.CoinsOffline +func (m *GetPortfolioSummaryResponse) GetCoinsOffline() []*Coin { + if m != nil { + return m.CoinsOffline } return nil } -func (x *GetPortfolioSummaryResponse) GetCoinsOfflineSummary() map[string]*OfflineCoins { - if x != nil { - return x.CoinsOfflineSummary +func (m *GetPortfolioSummaryResponse) GetCoinsOfflineSummary() map[string]*OfflineCoins { + if m != nil { + return m.CoinsOfflineSummary } return nil } -func (x *GetPortfolioSummaryResponse) GetCoinsOnline() []*Coin { - if x != nil { - return x.CoinsOnline +func (m *GetPortfolioSummaryResponse) GetCoinsOnline() []*Coin { + if m != nil { + return m.CoinsOnline } return nil } -func (x *GetPortfolioSummaryResponse) GetCoinsOnlineSummary() map[string]*OnlineCoins { - if x != nil { - return x.CoinsOnlineSummary +func (m *GetPortfolioSummaryResponse) GetCoinsOnlineSummary() map[string]*OnlineCoins { + if m != nil { + return m.CoinsOnlineSummary } return nil } type AddPortfolioAddressRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - CoinType string `protobuf:"bytes,2,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Balance float64 `protobuf:"fixed64,4,opt,name=balance,proto3" json:"balance,omitempty"` - SupportedExchanges string `protobuf:"bytes,5,opt,name=supported_exchanges,json=supportedExchanges,proto3" json:"supported_exchanges,omitempty"` - ColdStorage bool `protobuf:"varint,6,opt,name=cold_storage,json=coldStorage,proto3" json:"cold_storage,omitempty"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + CoinType string `protobuf:"bytes,2,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Balance float64 `protobuf:"fixed64,4,opt,name=balance,proto3" json:"balance,omitempty"` + SupportedExchanges string `protobuf:"bytes,5,opt,name=supported_exchanges,json=supportedExchanges,proto3" json:"supported_exchanges,omitempty"` + ColdStorage bool `protobuf:"varint,6,opt,name=cold_storage,json=coldStorage,proto3" json:"cold_storage,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *AddPortfolioAddressRequest) Reset() { - *x = AddPortfolioAddressRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[50] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AddPortfolioAddressRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddPortfolioAddressRequest) ProtoMessage() {} - -func (x *AddPortfolioAddressRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[50] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddPortfolioAddressRequest.ProtoReflect.Descriptor instead. +func (m *AddPortfolioAddressRequest) Reset() { *m = AddPortfolioAddressRequest{} } +func (m *AddPortfolioAddressRequest) String() string { return proto.CompactTextString(m) } +func (*AddPortfolioAddressRequest) ProtoMessage() {} func (*AddPortfolioAddressRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{50} + return fileDescriptor_77a6da22d6a3feb1, []int{48} } -func (x *AddPortfolioAddressRequest) GetAddress() string { - if x != nil { - return x.Address +func (m *AddPortfolioAddressRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddPortfolioAddressRequest.Unmarshal(m, b) +} +func (m *AddPortfolioAddressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddPortfolioAddressRequest.Marshal(b, m, deterministic) +} +func (m *AddPortfolioAddressRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddPortfolioAddressRequest.Merge(m, src) +} +func (m *AddPortfolioAddressRequest) XXX_Size() int { + return xxx_messageInfo_AddPortfolioAddressRequest.Size(m) +} +func (m *AddPortfolioAddressRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddPortfolioAddressRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddPortfolioAddressRequest proto.InternalMessageInfo + +func (m *AddPortfolioAddressRequest) GetAddress() string { + if m != nil { + return m.Address } return "" } -func (x *AddPortfolioAddressRequest) GetCoinType() string { - if x != nil { - return x.CoinType +func (m *AddPortfolioAddressRequest) GetCoinType() string { + if m != nil { + return m.CoinType } return "" } -func (x *AddPortfolioAddressRequest) GetDescription() string { - if x != nil { - return x.Description +func (m *AddPortfolioAddressRequest) GetDescription() string { + if m != nil { + return m.Description } return "" } -func (x *AddPortfolioAddressRequest) GetBalance() float64 { - if x != nil { - return x.Balance +func (m *AddPortfolioAddressRequest) GetBalance() float64 { + if m != nil { + return m.Balance } return 0 } -func (x *AddPortfolioAddressRequest) GetSupportedExchanges() string { - if x != nil { - return x.SupportedExchanges +func (m *AddPortfolioAddressRequest) GetSupportedExchanges() string { + if m != nil { + return m.SupportedExchanges } return "" } -func (x *AddPortfolioAddressRequest) GetColdStorage() bool { - if x != nil { - return x.ColdStorage +func (m *AddPortfolioAddressRequest) GetColdStorage() bool { + if m != nil { + return m.ColdStorage } return false } -type AddPortfolioAddressResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *AddPortfolioAddressResponse) Reset() { - *x = AddPortfolioAddressResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[51] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AddPortfolioAddressResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddPortfolioAddressResponse) ProtoMessage() {} - -func (x *AddPortfolioAddressResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[51] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddPortfolioAddressResponse.ProtoReflect.Descriptor instead. -func (*AddPortfolioAddressResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{51} -} - type RemovePortfolioAddressRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - CoinType string `protobuf:"bytes,2,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + CoinType string `protobuf:"bytes,2,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *RemovePortfolioAddressRequest) Reset() { - *x = RemovePortfolioAddressRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[52] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RemovePortfolioAddressRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemovePortfolioAddressRequest) ProtoMessage() {} - -func (x *RemovePortfolioAddressRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[52] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemovePortfolioAddressRequest.ProtoReflect.Descriptor instead. +func (m *RemovePortfolioAddressRequest) Reset() { *m = RemovePortfolioAddressRequest{} } +func (m *RemovePortfolioAddressRequest) String() string { return proto.CompactTextString(m) } +func (*RemovePortfolioAddressRequest) ProtoMessage() {} func (*RemovePortfolioAddressRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{52} + return fileDescriptor_77a6da22d6a3feb1, []int{49} } -func (x *RemovePortfolioAddressRequest) GetAddress() string { - if x != nil { - return x.Address +func (m *RemovePortfolioAddressRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemovePortfolioAddressRequest.Unmarshal(m, b) +} +func (m *RemovePortfolioAddressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemovePortfolioAddressRequest.Marshal(b, m, deterministic) +} +func (m *RemovePortfolioAddressRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemovePortfolioAddressRequest.Merge(m, src) +} +func (m *RemovePortfolioAddressRequest) XXX_Size() int { + return xxx_messageInfo_RemovePortfolioAddressRequest.Size(m) +} +func (m *RemovePortfolioAddressRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemovePortfolioAddressRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemovePortfolioAddressRequest proto.InternalMessageInfo + +func (m *RemovePortfolioAddressRequest) GetAddress() string { + if m != nil { + return m.Address } return "" } -func (x *RemovePortfolioAddressRequest) GetCoinType() string { - if x != nil { - return x.CoinType +func (m *RemovePortfolioAddressRequest) GetCoinType() string { + if m != nil { + return m.CoinType } return "" } -func (x *RemovePortfolioAddressRequest) GetDescription() string { - if x != nil { - return x.Description +func (m *RemovePortfolioAddressRequest) GetDescription() string { + if m != nil { + return m.Description } return "" } -type RemovePortfolioAddressResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *RemovePortfolioAddressResponse) Reset() { - *x = RemovePortfolioAddressResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[53] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RemovePortfolioAddressResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemovePortfolioAddressResponse) ProtoMessage() {} - -func (x *RemovePortfolioAddressResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[53] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemovePortfolioAddressResponse.ProtoReflect.Descriptor instead. -func (*RemovePortfolioAddressResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{53} -} - type GetForexProvidersRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetForexProvidersRequest) Reset() { - *x = GetForexProvidersRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[54] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetForexProvidersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetForexProvidersRequest) ProtoMessage() {} - -func (x *GetForexProvidersRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[54] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetForexProvidersRequest.ProtoReflect.Descriptor instead. +func (m *GetForexProvidersRequest) Reset() { *m = GetForexProvidersRequest{} } +func (m *GetForexProvidersRequest) String() string { return proto.CompactTextString(m) } +func (*GetForexProvidersRequest) ProtoMessage() {} func (*GetForexProvidersRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{54} + return fileDescriptor_77a6da22d6a3feb1, []int{50} } +func (m *GetForexProvidersRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetForexProvidersRequest.Unmarshal(m, b) +} +func (m *GetForexProvidersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetForexProvidersRequest.Marshal(b, m, deterministic) +} +func (m *GetForexProvidersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetForexProvidersRequest.Merge(m, src) +} +func (m *GetForexProvidersRequest) XXX_Size() int { + return xxx_messageInfo_GetForexProvidersRequest.Size(m) +} +func (m *GetForexProvidersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetForexProvidersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetForexProvidersRequest proto.InternalMessageInfo + type ForexProvider struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` - Verbose bool `protobuf:"varint,3,opt,name=verbose,proto3" json:"verbose,omitempty"` - RestPollingDelay string `protobuf:"bytes,4,opt,name=rest_polling_delay,json=restPollingDelay,proto3" json:"rest_polling_delay,omitempty"` - ApiKey string `protobuf:"bytes,5,opt,name=api_key,json=apiKey,proto3" json:"api_key,omitempty"` - ApiKeyLevel int64 `protobuf:"varint,6,opt,name=api_key_level,json=apiKeyLevel,proto3" json:"api_key_level,omitempty"` - PrimaryProvider bool `protobuf:"varint,7,opt,name=primary_provider,json=primaryProvider,proto3" json:"primary_provider,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Enabled bool `protobuf:"varint,2,opt,name=enabled,proto3" json:"enabled,omitempty"` + Verbose bool `protobuf:"varint,3,opt,name=verbose,proto3" json:"verbose,omitempty"` + RestPollingDelay string `protobuf:"bytes,4,opt,name=rest_polling_delay,json=restPollingDelay,proto3" json:"rest_polling_delay,omitempty"` + ApiKey string `protobuf:"bytes,5,opt,name=api_key,json=apiKey,proto3" json:"api_key,omitempty"` + ApiKeyLevel int64 `protobuf:"varint,6,opt,name=api_key_level,json=apiKeyLevel,proto3" json:"api_key_level,omitempty"` + PrimaryProvider bool `protobuf:"varint,7,opt,name=primary_provider,json=primaryProvider,proto3" json:"primary_provider,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *ForexProvider) Reset() { - *x = ForexProvider{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[55] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ForexProvider) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ForexProvider) ProtoMessage() {} - -func (x *ForexProvider) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[55] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ForexProvider.ProtoReflect.Descriptor instead. +func (m *ForexProvider) Reset() { *m = ForexProvider{} } +func (m *ForexProvider) String() string { return proto.CompactTextString(m) } +func (*ForexProvider) ProtoMessage() {} func (*ForexProvider) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{55} + return fileDescriptor_77a6da22d6a3feb1, []int{51} } -func (x *ForexProvider) GetName() string { - if x != nil { - return x.Name +func (m *ForexProvider) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ForexProvider.Unmarshal(m, b) +} +func (m *ForexProvider) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ForexProvider.Marshal(b, m, deterministic) +} +func (m *ForexProvider) XXX_Merge(src proto.Message) { + xxx_messageInfo_ForexProvider.Merge(m, src) +} +func (m *ForexProvider) XXX_Size() int { + return xxx_messageInfo_ForexProvider.Size(m) +} +func (m *ForexProvider) XXX_DiscardUnknown() { + xxx_messageInfo_ForexProvider.DiscardUnknown(m) +} + +var xxx_messageInfo_ForexProvider proto.InternalMessageInfo + +func (m *ForexProvider) GetName() string { + if m != nil { + return m.Name } return "" } -func (x *ForexProvider) GetEnabled() bool { - if x != nil { - return x.Enabled +func (m *ForexProvider) GetEnabled() bool { + if m != nil { + return m.Enabled } return false } -func (x *ForexProvider) GetVerbose() bool { - if x != nil { - return x.Verbose +func (m *ForexProvider) GetVerbose() bool { + if m != nil { + return m.Verbose } return false } -func (x *ForexProvider) GetRestPollingDelay() string { - if x != nil { - return x.RestPollingDelay +func (m *ForexProvider) GetRestPollingDelay() string { + if m != nil { + return m.RestPollingDelay } return "" } -func (x *ForexProvider) GetApiKey() string { - if x != nil { - return x.ApiKey +func (m *ForexProvider) GetApiKey() string { + if m != nil { + return m.ApiKey } return "" } -func (x *ForexProvider) GetApiKeyLevel() int64 { - if x != nil { - return x.ApiKeyLevel +func (m *ForexProvider) GetApiKeyLevel() int64 { + if m != nil { + return m.ApiKeyLevel } return 0 } -func (x *ForexProvider) GetPrimaryProvider() bool { - if x != nil { - return x.PrimaryProvider +func (m *ForexProvider) GetPrimaryProvider() bool { + if m != nil { + return m.PrimaryProvider } return false } type GetForexProvidersResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ForexProviders []*ForexProvider `protobuf:"bytes,1,rep,name=forex_providers,json=forexProviders,proto3" json:"forex_providers,omitempty"` + ForexProviders []*ForexProvider `protobuf:"bytes,1,rep,name=forex_providers,json=forexProviders,proto3" json:"forex_providers,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetForexProvidersResponse) Reset() { - *x = GetForexProvidersResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[56] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetForexProvidersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetForexProvidersResponse) ProtoMessage() {} - -func (x *GetForexProvidersResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[56] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetForexProvidersResponse.ProtoReflect.Descriptor instead. +func (m *GetForexProvidersResponse) Reset() { *m = GetForexProvidersResponse{} } +func (m *GetForexProvidersResponse) String() string { return proto.CompactTextString(m) } +func (*GetForexProvidersResponse) ProtoMessage() {} func (*GetForexProvidersResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{56} + return fileDescriptor_77a6da22d6a3feb1, []int{52} } -func (x *GetForexProvidersResponse) GetForexProviders() []*ForexProvider { - if x != nil { - return x.ForexProviders +func (m *GetForexProvidersResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetForexProvidersResponse.Unmarshal(m, b) +} +func (m *GetForexProvidersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetForexProvidersResponse.Marshal(b, m, deterministic) +} +func (m *GetForexProvidersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetForexProvidersResponse.Merge(m, src) +} +func (m *GetForexProvidersResponse) XXX_Size() int { + return xxx_messageInfo_GetForexProvidersResponse.Size(m) +} +func (m *GetForexProvidersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetForexProvidersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetForexProvidersResponse proto.InternalMessageInfo + +func (m *GetForexProvidersResponse) GetForexProviders() []*ForexProvider { + if m != nil { + return m.ForexProviders } return nil } type GetForexRatesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetForexRatesRequest) Reset() { - *x = GetForexRatesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[57] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetForexRatesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetForexRatesRequest) ProtoMessage() {} - -func (x *GetForexRatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[57] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetForexRatesRequest.ProtoReflect.Descriptor instead. +func (m *GetForexRatesRequest) Reset() { *m = GetForexRatesRequest{} } +func (m *GetForexRatesRequest) String() string { return proto.CompactTextString(m) } +func (*GetForexRatesRequest) ProtoMessage() {} func (*GetForexRatesRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{57} + return fileDescriptor_77a6da22d6a3feb1, []int{53} } +func (m *GetForexRatesRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetForexRatesRequest.Unmarshal(m, b) +} +func (m *GetForexRatesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetForexRatesRequest.Marshal(b, m, deterministic) +} +func (m *GetForexRatesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetForexRatesRequest.Merge(m, src) +} +func (m *GetForexRatesRequest) XXX_Size() int { + return xxx_messageInfo_GetForexRatesRequest.Size(m) +} +func (m *GetForexRatesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetForexRatesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetForexRatesRequest proto.InternalMessageInfo + type ForexRatesConversion struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` - To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` - Rate float64 `protobuf:"fixed64,3,opt,name=rate,proto3" json:"rate,omitempty"` - InverseRate float64 `protobuf:"fixed64,4,opt,name=inverse_rate,json=inverseRate,proto3" json:"inverse_rate,omitempty"` + From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` + Rate float64 `protobuf:"fixed64,3,opt,name=rate,proto3" json:"rate,omitempty"` + InverseRate float64 `protobuf:"fixed64,4,opt,name=inverse_rate,json=inverseRate,proto3" json:"inverse_rate,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *ForexRatesConversion) Reset() { - *x = ForexRatesConversion{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[58] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ForexRatesConversion) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ForexRatesConversion) ProtoMessage() {} - -func (x *ForexRatesConversion) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[58] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ForexRatesConversion.ProtoReflect.Descriptor instead. +func (m *ForexRatesConversion) Reset() { *m = ForexRatesConversion{} } +func (m *ForexRatesConversion) String() string { return proto.CompactTextString(m) } +func (*ForexRatesConversion) ProtoMessage() {} func (*ForexRatesConversion) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{58} + return fileDescriptor_77a6da22d6a3feb1, []int{54} } -func (x *ForexRatesConversion) GetFrom() string { - if x != nil { - return x.From +func (m *ForexRatesConversion) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ForexRatesConversion.Unmarshal(m, b) +} +func (m *ForexRatesConversion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ForexRatesConversion.Marshal(b, m, deterministic) +} +func (m *ForexRatesConversion) XXX_Merge(src proto.Message) { + xxx_messageInfo_ForexRatesConversion.Merge(m, src) +} +func (m *ForexRatesConversion) XXX_Size() int { + return xxx_messageInfo_ForexRatesConversion.Size(m) +} +func (m *ForexRatesConversion) XXX_DiscardUnknown() { + xxx_messageInfo_ForexRatesConversion.DiscardUnknown(m) +} + +var xxx_messageInfo_ForexRatesConversion proto.InternalMessageInfo + +func (m *ForexRatesConversion) GetFrom() string { + if m != nil { + return m.From } return "" } -func (x *ForexRatesConversion) GetTo() string { - if x != nil { - return x.To +func (m *ForexRatesConversion) GetTo() string { + if m != nil { + return m.To } return "" } -func (x *ForexRatesConversion) GetRate() float64 { - if x != nil { - return x.Rate +func (m *ForexRatesConversion) GetRate() float64 { + if m != nil { + return m.Rate } return 0 } -func (x *ForexRatesConversion) GetInverseRate() float64 { - if x != nil { - return x.InverseRate +func (m *ForexRatesConversion) GetInverseRate() float64 { + if m != nil { + return m.InverseRate } return 0 } type GetForexRatesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ForexRates []*ForexRatesConversion `protobuf:"bytes,1,rep,name=forex_rates,json=forexRates,proto3" json:"forex_rates,omitempty"` + ForexRates []*ForexRatesConversion `protobuf:"bytes,1,rep,name=forex_rates,json=forexRates,proto3" json:"forex_rates,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetForexRatesResponse) Reset() { - *x = GetForexRatesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[59] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetForexRatesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetForexRatesResponse) ProtoMessage() {} - -func (x *GetForexRatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[59] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetForexRatesResponse.ProtoReflect.Descriptor instead. +func (m *GetForexRatesResponse) Reset() { *m = GetForexRatesResponse{} } +func (m *GetForexRatesResponse) String() string { return proto.CompactTextString(m) } +func (*GetForexRatesResponse) ProtoMessage() {} func (*GetForexRatesResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{59} + return fileDescriptor_77a6da22d6a3feb1, []int{55} } -func (x *GetForexRatesResponse) GetForexRates() []*ForexRatesConversion { - if x != nil { - return x.ForexRates +func (m *GetForexRatesResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetForexRatesResponse.Unmarshal(m, b) +} +func (m *GetForexRatesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetForexRatesResponse.Marshal(b, m, deterministic) +} +func (m *GetForexRatesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetForexRatesResponse.Merge(m, src) +} +func (m *GetForexRatesResponse) XXX_Size() int { + return xxx_messageInfo_GetForexRatesResponse.Size(m) +} +func (m *GetForexRatesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetForexRatesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetForexRatesResponse proto.InternalMessageInfo + +func (m *GetForexRatesResponse) GetForexRates() []*ForexRatesConversion { + if m != nil { + return m.ForexRates } return nil } type OrderDetails struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - BaseCurrency string `protobuf:"bytes,3,opt,name=base_currency,json=baseCurrency,proto3" json:"base_currency,omitempty"` - QuoteCurrency string `protobuf:"bytes,4,opt,name=quote_currency,json=quoteCurrency,proto3" json:"quote_currency,omitempty"` - AssetType string `protobuf:"bytes,5,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` - OrderSide string `protobuf:"bytes,6,opt,name=order_side,json=orderSide,proto3" json:"order_side,omitempty"` - OrderType string `protobuf:"bytes,7,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` - CreationTime int64 `protobuf:"varint,8,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` - Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` - Price float64 `protobuf:"fixed64,10,opt,name=price,proto3" json:"price,omitempty"` - Amount float64 `protobuf:"fixed64,11,opt,name=amount,proto3" json:"amount,omitempty"` - OpenVolume float64 `protobuf:"fixed64,12,opt,name=open_volume,json=openVolume,proto3" json:"open_volume,omitempty"` - Fee float64 `protobuf:"fixed64,13,opt,name=fee,proto3" json:"fee,omitempty"` - Trades []*TradeHistory `protobuf:"bytes,14,rep,name=trades,proto3" json:"trades,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + BaseCurrency string `protobuf:"bytes,3,opt,name=base_currency,json=baseCurrency,proto3" json:"base_currency,omitempty"` + QuoteCurrency string `protobuf:"bytes,4,opt,name=quote_currency,json=quoteCurrency,proto3" json:"quote_currency,omitempty"` + AssetType string `protobuf:"bytes,5,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + OrderSide string `protobuf:"bytes,6,opt,name=order_side,json=orderSide,proto3" json:"order_side,omitempty"` + OrderType string `protobuf:"bytes,7,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` + CreationTime int64 `protobuf:"varint,8,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` + Status string `protobuf:"bytes,9,opt,name=status,proto3" json:"status,omitempty"` + Price float64 `protobuf:"fixed64,10,opt,name=price,proto3" json:"price,omitempty"` + Amount float64 `protobuf:"fixed64,11,opt,name=amount,proto3" json:"amount,omitempty"` + OpenVolume float64 `protobuf:"fixed64,12,opt,name=open_volume,json=openVolume,proto3" json:"open_volume,omitempty"` + Fee float64 `protobuf:"fixed64,13,opt,name=fee,proto3" json:"fee,omitempty"` + Trades []*TradeHistory `protobuf:"bytes,14,rep,name=trades,proto3" json:"trades,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *OrderDetails) Reset() { - *x = OrderDetails{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[60] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *OrderDetails) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*OrderDetails) ProtoMessage() {} - -func (x *OrderDetails) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[60] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use OrderDetails.ProtoReflect.Descriptor instead. +func (m *OrderDetails) Reset() { *m = OrderDetails{} } +func (m *OrderDetails) String() string { return proto.CompactTextString(m) } +func (*OrderDetails) ProtoMessage() {} func (*OrderDetails) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{60} + return fileDescriptor_77a6da22d6a3feb1, []int{56} } -func (x *OrderDetails) GetExchange() string { - if x != nil { - return x.Exchange +func (m *OrderDetails) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_OrderDetails.Unmarshal(m, b) +} +func (m *OrderDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_OrderDetails.Marshal(b, m, deterministic) +} +func (m *OrderDetails) XXX_Merge(src proto.Message) { + xxx_messageInfo_OrderDetails.Merge(m, src) +} +func (m *OrderDetails) XXX_Size() int { + return xxx_messageInfo_OrderDetails.Size(m) +} +func (m *OrderDetails) XXX_DiscardUnknown() { + xxx_messageInfo_OrderDetails.DiscardUnknown(m) +} + +var xxx_messageInfo_OrderDetails proto.InternalMessageInfo + +func (m *OrderDetails) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *OrderDetails) GetId() string { - if x != nil { - return x.Id +func (m *OrderDetails) GetId() string { + if m != nil { + return m.Id } return "" } -func (x *OrderDetails) GetBaseCurrency() string { - if x != nil { - return x.BaseCurrency +func (m *OrderDetails) GetBaseCurrency() string { + if m != nil { + return m.BaseCurrency } return "" } -func (x *OrderDetails) GetQuoteCurrency() string { - if x != nil { - return x.QuoteCurrency +func (m *OrderDetails) GetQuoteCurrency() string { + if m != nil { + return m.QuoteCurrency } return "" } -func (x *OrderDetails) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *OrderDetails) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } -func (x *OrderDetails) GetOrderSide() string { - if x != nil { - return x.OrderSide +func (m *OrderDetails) GetOrderSide() string { + if m != nil { + return m.OrderSide } return "" } -func (x *OrderDetails) GetOrderType() string { - if x != nil { - return x.OrderType +func (m *OrderDetails) GetOrderType() string { + if m != nil { + return m.OrderType } return "" } -func (x *OrderDetails) GetCreationTime() int64 { - if x != nil { - return x.CreationTime +func (m *OrderDetails) GetCreationTime() int64 { + if m != nil { + return m.CreationTime } return 0 } -func (x *OrderDetails) GetStatus() string { - if x != nil { - return x.Status +func (m *OrderDetails) GetStatus() string { + if m != nil { + return m.Status } return "" } -func (x *OrderDetails) GetPrice() float64 { - if x != nil { - return x.Price +func (m *OrderDetails) GetPrice() float64 { + if m != nil { + return m.Price } return 0 } -func (x *OrderDetails) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *OrderDetails) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *OrderDetails) GetOpenVolume() float64 { - if x != nil { - return x.OpenVolume +func (m *OrderDetails) GetOpenVolume() float64 { + if m != nil { + return m.OpenVolume } return 0 } -func (x *OrderDetails) GetFee() float64 { - if x != nil { - return x.Fee +func (m *OrderDetails) GetFee() float64 { + if m != nil { + return m.Fee } return 0 } -func (x *OrderDetails) GetTrades() []*TradeHistory { - if x != nil { - return x.Trades +func (m *OrderDetails) GetTrades() []*TradeHistory { + if m != nil { + return m.Trades } return nil } type TradeHistory struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - CreationTime int64 `protobuf:"varint,1,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Price float64 `protobuf:"fixed64,3,opt,name=price,proto3" json:"price,omitempty"` - Amount float64 `protobuf:"fixed64,4,opt,name=amount,proto3" json:"amount,omitempty"` - Exchange string `protobuf:"bytes,5,opt,name=exchange,proto3" json:"exchange,omitempty"` - AssetType string `protobuf:"bytes,6,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` - OrderSide string `protobuf:"bytes,7,opt,name=order_side,json=orderSide,proto3" json:"order_side,omitempty"` - Fee float64 `protobuf:"fixed64,8,opt,name=fee,proto3" json:"fee,omitempty"` + CreationTime int64 `protobuf:"varint,1,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Price float64 `protobuf:"fixed64,3,opt,name=price,proto3" json:"price,omitempty"` + Amount float64 `protobuf:"fixed64,4,opt,name=amount,proto3" json:"amount,omitempty"` + Exchange string `protobuf:"bytes,5,opt,name=exchange,proto3" json:"exchange,omitempty"` + AssetType string `protobuf:"bytes,6,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + OrderSide string `protobuf:"bytes,7,opt,name=order_side,json=orderSide,proto3" json:"order_side,omitempty"` + Fee float64 `protobuf:"fixed64,8,opt,name=fee,proto3" json:"fee,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *TradeHistory) Reset() { - *x = TradeHistory{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[61] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TradeHistory) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TradeHistory) ProtoMessage() {} - -func (x *TradeHistory) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[61] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TradeHistory.ProtoReflect.Descriptor instead. +func (m *TradeHistory) Reset() { *m = TradeHistory{} } +func (m *TradeHistory) String() string { return proto.CompactTextString(m) } +func (*TradeHistory) ProtoMessage() {} func (*TradeHistory) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{61} + return fileDescriptor_77a6da22d6a3feb1, []int{57} } -func (x *TradeHistory) GetCreationTime() int64 { - if x != nil { - return x.CreationTime +func (m *TradeHistory) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TradeHistory.Unmarshal(m, b) +} +func (m *TradeHistory) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TradeHistory.Marshal(b, m, deterministic) +} +func (m *TradeHistory) XXX_Merge(src proto.Message) { + xxx_messageInfo_TradeHistory.Merge(m, src) +} +func (m *TradeHistory) XXX_Size() int { + return xxx_messageInfo_TradeHistory.Size(m) +} +func (m *TradeHistory) XXX_DiscardUnknown() { + xxx_messageInfo_TradeHistory.DiscardUnknown(m) +} + +var xxx_messageInfo_TradeHistory proto.InternalMessageInfo + +func (m *TradeHistory) GetCreationTime() int64 { + if m != nil { + return m.CreationTime } return 0 } -func (x *TradeHistory) GetId() string { - if x != nil { - return x.Id +func (m *TradeHistory) GetId() string { + if m != nil { + return m.Id } return "" } -func (x *TradeHistory) GetPrice() float64 { - if x != nil { - return x.Price +func (m *TradeHistory) GetPrice() float64 { + if m != nil { + return m.Price } return 0 } -func (x *TradeHistory) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *TradeHistory) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *TradeHistory) GetExchange() string { - if x != nil { - return x.Exchange +func (m *TradeHistory) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *TradeHistory) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *TradeHistory) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } -func (x *TradeHistory) GetOrderSide() string { - if x != nil { - return x.OrderSide +func (m *TradeHistory) GetOrderSide() string { + if m != nil { + return m.OrderSide } return "" } -func (x *TradeHistory) GetFee() float64 { - if x != nil { - return x.Fee +func (m *TradeHistory) GetFee() float64 { + if m != nil { + return m.Fee } return 0 } type GetOrdersRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - AssetType string `protobuf:"bytes,2,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,3,opt,name=pair,proto3" json:"pair,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + AssetType string `protobuf:"bytes,2,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,3,opt,name=pair,proto3" json:"pair,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetOrdersRequest) Reset() { - *x = GetOrdersRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[62] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetOrdersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetOrdersRequest) ProtoMessage() {} - -func (x *GetOrdersRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[62] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetOrdersRequest.ProtoReflect.Descriptor instead. +func (m *GetOrdersRequest) Reset() { *m = GetOrdersRequest{} } +func (m *GetOrdersRequest) String() string { return proto.CompactTextString(m) } +func (*GetOrdersRequest) ProtoMessage() {} func (*GetOrdersRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{62} + return fileDescriptor_77a6da22d6a3feb1, []int{58} } -func (x *GetOrdersRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetOrdersRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetOrdersRequest.Unmarshal(m, b) +} +func (m *GetOrdersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetOrdersRequest.Marshal(b, m, deterministic) +} +func (m *GetOrdersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetOrdersRequest.Merge(m, src) +} +func (m *GetOrdersRequest) XXX_Size() int { + return xxx_messageInfo_GetOrdersRequest.Size(m) +} +func (m *GetOrdersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetOrdersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetOrdersRequest proto.InternalMessageInfo + +func (m *GetOrdersRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetOrdersRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *GetOrdersRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } -func (x *GetOrdersRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *GetOrdersRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } type GetOrdersResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Orders []*OrderDetails `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + Orders []*OrderDetails `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetOrdersResponse) Reset() { - *x = GetOrdersResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[63] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetOrdersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetOrdersResponse) ProtoMessage() {} - -func (x *GetOrdersResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[63] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetOrdersResponse.ProtoReflect.Descriptor instead. +func (m *GetOrdersResponse) Reset() { *m = GetOrdersResponse{} } +func (m *GetOrdersResponse) String() string { return proto.CompactTextString(m) } +func (*GetOrdersResponse) ProtoMessage() {} func (*GetOrdersResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{63} + return fileDescriptor_77a6da22d6a3feb1, []int{59} } -func (x *GetOrdersResponse) GetOrders() []*OrderDetails { - if x != nil { - return x.Orders +func (m *GetOrdersResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetOrdersResponse.Unmarshal(m, b) +} +func (m *GetOrdersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetOrdersResponse.Marshal(b, m, deterministic) +} +func (m *GetOrdersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetOrdersResponse.Merge(m, src) +} +func (m *GetOrdersResponse) XXX_Size() int { + return xxx_messageInfo_GetOrdersResponse.Size(m) +} +func (m *GetOrdersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetOrdersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetOrdersResponse proto.InternalMessageInfo + +func (m *GetOrdersResponse) GetOrders() []*OrderDetails { + if m != nil { + return m.Orders } return nil } type GetOrderRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - OrderId string `protobuf:"bytes,2,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + OrderId string `protobuf:"bytes,2,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetOrderRequest) Reset() { - *x = GetOrderRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[64] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetOrderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetOrderRequest) ProtoMessage() {} - -func (x *GetOrderRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[64] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetOrderRequest.ProtoReflect.Descriptor instead. +func (m *GetOrderRequest) Reset() { *m = GetOrderRequest{} } +func (m *GetOrderRequest) String() string { return proto.CompactTextString(m) } +func (*GetOrderRequest) ProtoMessage() {} func (*GetOrderRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{64} + return fileDescriptor_77a6da22d6a3feb1, []int{60} } -func (x *GetOrderRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetOrderRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetOrderRequest.Unmarshal(m, b) +} +func (m *GetOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetOrderRequest.Marshal(b, m, deterministic) +} +func (m *GetOrderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetOrderRequest.Merge(m, src) +} +func (m *GetOrderRequest) XXX_Size() int { + return xxx_messageInfo_GetOrderRequest.Size(m) +} +func (m *GetOrderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetOrderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetOrderRequest proto.InternalMessageInfo + +func (m *GetOrderRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetOrderRequest) GetOrderId() string { - if x != nil { - return x.OrderId +func (m *GetOrderRequest) GetOrderId() string { + if m != nil { + return m.OrderId } return "" } type SubmitOrderRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - Side string `protobuf:"bytes,3,opt,name=side,proto3" json:"side,omitempty"` - OrderType string `protobuf:"bytes,4,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` - Amount float64 `protobuf:"fixed64,5,opt,name=amount,proto3" json:"amount,omitempty"` - Price float64 `protobuf:"fixed64,6,opt,name=price,proto3" json:"price,omitempty"` - ClientId string `protobuf:"bytes,7,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + Side string `protobuf:"bytes,3,opt,name=side,proto3" json:"side,omitempty"` + OrderType string `protobuf:"bytes,4,opt,name=order_type,json=orderType,proto3" json:"order_type,omitempty"` + Amount float64 `protobuf:"fixed64,5,opt,name=amount,proto3" json:"amount,omitempty"` + Price float64 `protobuf:"fixed64,6,opt,name=price,proto3" json:"price,omitempty"` + ClientId string `protobuf:"bytes,7,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *SubmitOrderRequest) Reset() { - *x = SubmitOrderRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[65] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SubmitOrderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitOrderRequest) ProtoMessage() {} - -func (x *SubmitOrderRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[65] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubmitOrderRequest.ProtoReflect.Descriptor instead. +func (m *SubmitOrderRequest) Reset() { *m = SubmitOrderRequest{} } +func (m *SubmitOrderRequest) String() string { return proto.CompactTextString(m) } +func (*SubmitOrderRequest) ProtoMessage() {} func (*SubmitOrderRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{65} + return fileDescriptor_77a6da22d6a3feb1, []int{61} } -func (x *SubmitOrderRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *SubmitOrderRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SubmitOrderRequest.Unmarshal(m, b) +} +func (m *SubmitOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SubmitOrderRequest.Marshal(b, m, deterministic) +} +func (m *SubmitOrderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubmitOrderRequest.Merge(m, src) +} +func (m *SubmitOrderRequest) XXX_Size() int { + return xxx_messageInfo_SubmitOrderRequest.Size(m) +} +func (m *SubmitOrderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SubmitOrderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SubmitOrderRequest proto.InternalMessageInfo + +func (m *SubmitOrderRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *SubmitOrderRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *SubmitOrderRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *SubmitOrderRequest) GetSide() string { - if x != nil { - return x.Side +func (m *SubmitOrderRequest) GetSide() string { + if m != nil { + return m.Side } return "" } -func (x *SubmitOrderRequest) GetOrderType() string { - if x != nil { - return x.OrderType +func (m *SubmitOrderRequest) GetOrderType() string { + if m != nil { + return m.OrderType } return "" } -func (x *SubmitOrderRequest) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *SubmitOrderRequest) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *SubmitOrderRequest) GetPrice() float64 { - if x != nil { - return x.Price +func (m *SubmitOrderRequest) GetPrice() float64 { + if m != nil { + return m.Price } return 0 } -func (x *SubmitOrderRequest) GetClientId() string { - if x != nil { - return x.ClientId +func (m *SubmitOrderRequest) GetClientId() string { + if m != nil { + return m.ClientId } return "" } type SubmitOrderResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - OrderPlaced bool `protobuf:"varint,1,opt,name=order_placed,json=orderPlaced,proto3" json:"order_placed,omitempty"` - OrderId string `protobuf:"bytes,2,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + OrderPlaced bool `protobuf:"varint,1,opt,name=order_placed,json=orderPlaced,proto3" json:"order_placed,omitempty"` + OrderId string `protobuf:"bytes,2,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *SubmitOrderResponse) Reset() { - *x = SubmitOrderResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[66] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SubmitOrderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitOrderResponse) ProtoMessage() {} - -func (x *SubmitOrderResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[66] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubmitOrderResponse.ProtoReflect.Descriptor instead. +func (m *SubmitOrderResponse) Reset() { *m = SubmitOrderResponse{} } +func (m *SubmitOrderResponse) String() string { return proto.CompactTextString(m) } +func (*SubmitOrderResponse) ProtoMessage() {} func (*SubmitOrderResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{66} + return fileDescriptor_77a6da22d6a3feb1, []int{62} } -func (x *SubmitOrderResponse) GetOrderPlaced() bool { - if x != nil { - return x.OrderPlaced +func (m *SubmitOrderResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SubmitOrderResponse.Unmarshal(m, b) +} +func (m *SubmitOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SubmitOrderResponse.Marshal(b, m, deterministic) +} +func (m *SubmitOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubmitOrderResponse.Merge(m, src) +} +func (m *SubmitOrderResponse) XXX_Size() int { + return xxx_messageInfo_SubmitOrderResponse.Size(m) +} +func (m *SubmitOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SubmitOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SubmitOrderResponse proto.InternalMessageInfo + +func (m *SubmitOrderResponse) GetOrderPlaced() bool { + if m != nil { + return m.OrderPlaced } return false } -func (x *SubmitOrderResponse) GetOrderId() string { - if x != nil { - return x.OrderId +func (m *SubmitOrderResponse) GetOrderId() string { + if m != nil { + return m.OrderId } return "" } type SimulateOrderRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - Amount float64 `protobuf:"fixed64,3,opt,name=amount,proto3" json:"amount,omitempty"` - Side string `protobuf:"bytes,4,opt,name=side,proto3" json:"side,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + Amount float64 `protobuf:"fixed64,3,opt,name=amount,proto3" json:"amount,omitempty"` + Side string `protobuf:"bytes,4,opt,name=side,proto3" json:"side,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *SimulateOrderRequest) Reset() { - *x = SimulateOrderRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[67] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SimulateOrderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SimulateOrderRequest) ProtoMessage() {} - -func (x *SimulateOrderRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[67] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SimulateOrderRequest.ProtoReflect.Descriptor instead. +func (m *SimulateOrderRequest) Reset() { *m = SimulateOrderRequest{} } +func (m *SimulateOrderRequest) String() string { return proto.CompactTextString(m) } +func (*SimulateOrderRequest) ProtoMessage() {} func (*SimulateOrderRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{67} + return fileDescriptor_77a6da22d6a3feb1, []int{63} } -func (x *SimulateOrderRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *SimulateOrderRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SimulateOrderRequest.Unmarshal(m, b) +} +func (m *SimulateOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SimulateOrderRequest.Marshal(b, m, deterministic) +} +func (m *SimulateOrderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SimulateOrderRequest.Merge(m, src) +} +func (m *SimulateOrderRequest) XXX_Size() int { + return xxx_messageInfo_SimulateOrderRequest.Size(m) +} +func (m *SimulateOrderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SimulateOrderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SimulateOrderRequest proto.InternalMessageInfo + +func (m *SimulateOrderRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *SimulateOrderRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *SimulateOrderRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *SimulateOrderRequest) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *SimulateOrderRequest) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *SimulateOrderRequest) GetSide() string { - if x != nil { - return x.Side +func (m *SimulateOrderRequest) GetSide() string { + if m != nil { + return m.Side } return "" } type SimulateOrderResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Orders []*OrderbookItem `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` - Amount float64 `protobuf:"fixed64,2,opt,name=amount,proto3" json:"amount,omitempty"` - MinimumPrice float64 `protobuf:"fixed64,3,opt,name=minimum_price,json=minimumPrice,proto3" json:"minimum_price,omitempty"` - MaximumPrice float64 `protobuf:"fixed64,4,opt,name=maximum_price,json=maximumPrice,proto3" json:"maximum_price,omitempty"` - PercentageGainLoss float64 `protobuf:"fixed64,5,opt,name=percentage_gain_loss,json=percentageGainLoss,proto3" json:"percentage_gain_loss,omitempty"` - Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + Orders []*OrderbookItem `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + Amount float64 `protobuf:"fixed64,2,opt,name=amount,proto3" json:"amount,omitempty"` + MinimumPrice float64 `protobuf:"fixed64,3,opt,name=minimum_price,json=minimumPrice,proto3" json:"minimum_price,omitempty"` + MaximumPrice float64 `protobuf:"fixed64,4,opt,name=maximum_price,json=maximumPrice,proto3" json:"maximum_price,omitempty"` + PercentageGainLoss float64 `protobuf:"fixed64,5,opt,name=percentage_gain_loss,json=percentageGainLoss,proto3" json:"percentage_gain_loss,omitempty"` + Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *SimulateOrderResponse) Reset() { - *x = SimulateOrderResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[68] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SimulateOrderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SimulateOrderResponse) ProtoMessage() {} - -func (x *SimulateOrderResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[68] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SimulateOrderResponse.ProtoReflect.Descriptor instead. +func (m *SimulateOrderResponse) Reset() { *m = SimulateOrderResponse{} } +func (m *SimulateOrderResponse) String() string { return proto.CompactTextString(m) } +func (*SimulateOrderResponse) ProtoMessage() {} func (*SimulateOrderResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{68} + return fileDescriptor_77a6da22d6a3feb1, []int{64} } -func (x *SimulateOrderResponse) GetOrders() []*OrderbookItem { - if x != nil { - return x.Orders +func (m *SimulateOrderResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SimulateOrderResponse.Unmarshal(m, b) +} +func (m *SimulateOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SimulateOrderResponse.Marshal(b, m, deterministic) +} +func (m *SimulateOrderResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SimulateOrderResponse.Merge(m, src) +} +func (m *SimulateOrderResponse) XXX_Size() int { + return xxx_messageInfo_SimulateOrderResponse.Size(m) +} +func (m *SimulateOrderResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SimulateOrderResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SimulateOrderResponse proto.InternalMessageInfo + +func (m *SimulateOrderResponse) GetOrders() []*OrderbookItem { + if m != nil { + return m.Orders } return nil } -func (x *SimulateOrderResponse) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *SimulateOrderResponse) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *SimulateOrderResponse) GetMinimumPrice() float64 { - if x != nil { - return x.MinimumPrice +func (m *SimulateOrderResponse) GetMinimumPrice() float64 { + if m != nil { + return m.MinimumPrice } return 0 } -func (x *SimulateOrderResponse) GetMaximumPrice() float64 { - if x != nil { - return x.MaximumPrice +func (m *SimulateOrderResponse) GetMaximumPrice() float64 { + if m != nil { + return m.MaximumPrice } return 0 } -func (x *SimulateOrderResponse) GetPercentageGainLoss() float64 { - if x != nil { - return x.PercentageGainLoss +func (m *SimulateOrderResponse) GetPercentageGainLoss() float64 { + if m != nil { + return m.PercentageGainLoss } return 0 } -func (x *SimulateOrderResponse) GetStatus() string { - if x != nil { - return x.Status +func (m *SimulateOrderResponse) GetStatus() string { + if m != nil { + return m.Status } return "" } type WhaleBombRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - PriceTarget float64 `protobuf:"fixed64,3,opt,name=price_target,json=priceTarget,proto3" json:"price_target,omitempty"` - Side string `protobuf:"bytes,4,opt,name=side,proto3" json:"side,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + PriceTarget float64 `protobuf:"fixed64,3,opt,name=price_target,json=priceTarget,proto3" json:"price_target,omitempty"` + Side string `protobuf:"bytes,4,opt,name=side,proto3" json:"side,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WhaleBombRequest) Reset() { - *x = WhaleBombRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[69] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WhaleBombRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WhaleBombRequest) ProtoMessage() {} - -func (x *WhaleBombRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[69] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WhaleBombRequest.ProtoReflect.Descriptor instead. +func (m *WhaleBombRequest) Reset() { *m = WhaleBombRequest{} } +func (m *WhaleBombRequest) String() string { return proto.CompactTextString(m) } +func (*WhaleBombRequest) ProtoMessage() {} func (*WhaleBombRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{69} + return fileDescriptor_77a6da22d6a3feb1, []int{65} } -func (x *WhaleBombRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *WhaleBombRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WhaleBombRequest.Unmarshal(m, b) +} +func (m *WhaleBombRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WhaleBombRequest.Marshal(b, m, deterministic) +} +func (m *WhaleBombRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WhaleBombRequest.Merge(m, src) +} +func (m *WhaleBombRequest) XXX_Size() int { + return xxx_messageInfo_WhaleBombRequest.Size(m) +} +func (m *WhaleBombRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WhaleBombRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WhaleBombRequest proto.InternalMessageInfo + +func (m *WhaleBombRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *WhaleBombRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *WhaleBombRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *WhaleBombRequest) GetPriceTarget() float64 { - if x != nil { - return x.PriceTarget +func (m *WhaleBombRequest) GetPriceTarget() float64 { + if m != nil { + return m.PriceTarget } return 0 } -func (x *WhaleBombRequest) GetSide() string { - if x != nil { - return x.Side +func (m *WhaleBombRequest) GetSide() string { + if m != nil { + return m.Side } return "" } type CancelOrderRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - AccountId string `protobuf:"bytes,2,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` - OrderId string `protobuf:"bytes,3,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,4,opt,name=pair,proto3" json:"pair,omitempty"` - AssetType string `protobuf:"bytes,5,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` - WalletAddress string `protobuf:"bytes,6,opt,name=wallet_address,json=walletAddress,proto3" json:"wallet_address,omitempty"` - Side string `protobuf:"bytes,7,opt,name=side,proto3" json:"side,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + AccountId string `protobuf:"bytes,2,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` + OrderId string `protobuf:"bytes,3,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,4,opt,name=pair,proto3" json:"pair,omitempty"` + AssetType string `protobuf:"bytes,5,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + WalletAddress string `protobuf:"bytes,6,opt,name=wallet_address,json=walletAddress,proto3" json:"wallet_address,omitempty"` + Side string `protobuf:"bytes,7,opt,name=side,proto3" json:"side,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *CancelOrderRequest) Reset() { - *x = CancelOrderRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[70] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CancelOrderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CancelOrderRequest) ProtoMessage() {} - -func (x *CancelOrderRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[70] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelOrderRequest.ProtoReflect.Descriptor instead. +func (m *CancelOrderRequest) Reset() { *m = CancelOrderRequest{} } +func (m *CancelOrderRequest) String() string { return proto.CompactTextString(m) } +func (*CancelOrderRequest) ProtoMessage() {} func (*CancelOrderRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{70} + return fileDescriptor_77a6da22d6a3feb1, []int{66} } -func (x *CancelOrderRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *CancelOrderRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CancelOrderRequest.Unmarshal(m, b) +} +func (m *CancelOrderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CancelOrderRequest.Marshal(b, m, deterministic) +} +func (m *CancelOrderRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CancelOrderRequest.Merge(m, src) +} +func (m *CancelOrderRequest) XXX_Size() int { + return xxx_messageInfo_CancelOrderRequest.Size(m) +} +func (m *CancelOrderRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CancelOrderRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CancelOrderRequest proto.InternalMessageInfo + +func (m *CancelOrderRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *CancelOrderRequest) GetAccountId() string { - if x != nil { - return x.AccountId +func (m *CancelOrderRequest) GetAccountId() string { + if m != nil { + return m.AccountId } return "" } -func (x *CancelOrderRequest) GetOrderId() string { - if x != nil { - return x.OrderId +func (m *CancelOrderRequest) GetOrderId() string { + if m != nil { + return m.OrderId } return "" } -func (x *CancelOrderRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *CancelOrderRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *CancelOrderRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *CancelOrderRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } -func (x *CancelOrderRequest) GetWalletAddress() string { - if x != nil { - return x.WalletAddress +func (m *CancelOrderRequest) GetWalletAddress() string { + if m != nil { + return m.WalletAddress } return "" } -func (x *CancelOrderRequest) GetSide() string { - if x != nil { - return x.Side +func (m *CancelOrderRequest) GetSide() string { + if m != nil { + return m.Side } return "" } -type CancelOrderResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *CancelOrderResponse) Reset() { - *x = CancelOrderResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[71] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CancelOrderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CancelOrderResponse) ProtoMessage() {} - -func (x *CancelOrderResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[71] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelOrderResponse.ProtoReflect.Descriptor instead. -func (*CancelOrderResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{71} -} - type CancelAllOrdersRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *CancelAllOrdersRequest) Reset() { - *x = CancelAllOrdersRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[72] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CancelAllOrdersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CancelAllOrdersRequest) ProtoMessage() {} - -func (x *CancelAllOrdersRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[72] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelAllOrdersRequest.ProtoReflect.Descriptor instead. +func (m *CancelAllOrdersRequest) Reset() { *m = CancelAllOrdersRequest{} } +func (m *CancelAllOrdersRequest) String() string { return proto.CompactTextString(m) } +func (*CancelAllOrdersRequest) ProtoMessage() {} func (*CancelAllOrdersRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{72} + return fileDescriptor_77a6da22d6a3feb1, []int{67} } -func (x *CancelAllOrdersRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *CancelAllOrdersRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CancelAllOrdersRequest.Unmarshal(m, b) +} +func (m *CancelAllOrdersRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CancelAllOrdersRequest.Marshal(b, m, deterministic) +} +func (m *CancelAllOrdersRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_CancelAllOrdersRequest.Merge(m, src) +} +func (m *CancelAllOrdersRequest) XXX_Size() int { + return xxx_messageInfo_CancelAllOrdersRequest.Size(m) +} +func (m *CancelAllOrdersRequest) XXX_DiscardUnknown() { + xxx_messageInfo_CancelAllOrdersRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_CancelAllOrdersRequest proto.InternalMessageInfo + +func (m *CancelAllOrdersRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } type CancelAllOrdersResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Orders []*CancelAllOrdersResponse_Orders `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + Orders []*CancelAllOrdersResponse_Orders `protobuf:"bytes,1,rep,name=orders,proto3" json:"orders,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *CancelAllOrdersResponse) Reset() { - *x = CancelAllOrdersResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[73] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CancelAllOrdersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CancelAllOrdersResponse) ProtoMessage() {} - -func (x *CancelAllOrdersResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[73] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelAllOrdersResponse.ProtoReflect.Descriptor instead. +func (m *CancelAllOrdersResponse) Reset() { *m = CancelAllOrdersResponse{} } +func (m *CancelAllOrdersResponse) String() string { return proto.CompactTextString(m) } +func (*CancelAllOrdersResponse) ProtoMessage() {} func (*CancelAllOrdersResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{73} + return fileDescriptor_77a6da22d6a3feb1, []int{68} } -func (x *CancelAllOrdersResponse) GetOrders() []*CancelAllOrdersResponse_Orders { - if x != nil { - return x.Orders +func (m *CancelAllOrdersResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CancelAllOrdersResponse.Unmarshal(m, b) +} +func (m *CancelAllOrdersResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CancelAllOrdersResponse.Marshal(b, m, deterministic) +} +func (m *CancelAllOrdersResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_CancelAllOrdersResponse.Merge(m, src) +} +func (m *CancelAllOrdersResponse) XXX_Size() int { + return xxx_messageInfo_CancelAllOrdersResponse.Size(m) +} +func (m *CancelAllOrdersResponse) XXX_DiscardUnknown() { + xxx_messageInfo_CancelAllOrdersResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_CancelAllOrdersResponse proto.InternalMessageInfo + +func (m *CancelAllOrdersResponse) GetOrders() []*CancelAllOrdersResponse_Orders { + if m != nil { + return m.Orders + } + return nil +} + +type CancelAllOrdersResponse_Orders struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + OrderStatus map[string]string `protobuf:"bytes,2,rep,name=order_status,json=orderStatus,proto3" json:"order_status,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CancelAllOrdersResponse_Orders) Reset() { *m = CancelAllOrdersResponse_Orders{} } +func (m *CancelAllOrdersResponse_Orders) String() string { return proto.CompactTextString(m) } +func (*CancelAllOrdersResponse_Orders) ProtoMessage() {} +func (*CancelAllOrdersResponse_Orders) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{68, 0} +} + +func (m *CancelAllOrdersResponse_Orders) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CancelAllOrdersResponse_Orders.Unmarshal(m, b) +} +func (m *CancelAllOrdersResponse_Orders) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CancelAllOrdersResponse_Orders.Marshal(b, m, deterministic) +} +func (m *CancelAllOrdersResponse_Orders) XXX_Merge(src proto.Message) { + xxx_messageInfo_CancelAllOrdersResponse_Orders.Merge(m, src) +} +func (m *CancelAllOrdersResponse_Orders) XXX_Size() int { + return xxx_messageInfo_CancelAllOrdersResponse_Orders.Size(m) +} +func (m *CancelAllOrdersResponse_Orders) XXX_DiscardUnknown() { + xxx_messageInfo_CancelAllOrdersResponse_Orders.DiscardUnknown(m) +} + +var xxx_messageInfo_CancelAllOrdersResponse_Orders proto.InternalMessageInfo + +func (m *CancelAllOrdersResponse_Orders) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +func (m *CancelAllOrdersResponse_Orders) GetOrderStatus() map[string]string { + if m != nil { + return m.OrderStatus } return nil } type GetEventsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetEventsRequest) Reset() { - *x = GetEventsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[74] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetEventsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetEventsRequest) ProtoMessage() {} - -func (x *GetEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[74] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetEventsRequest.ProtoReflect.Descriptor instead. +func (m *GetEventsRequest) Reset() { *m = GetEventsRequest{} } +func (m *GetEventsRequest) String() string { return proto.CompactTextString(m) } +func (*GetEventsRequest) ProtoMessage() {} func (*GetEventsRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{74} + return fileDescriptor_77a6da22d6a3feb1, []int{69} } +func (m *GetEventsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetEventsRequest.Unmarshal(m, b) +} +func (m *GetEventsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetEventsRequest.Marshal(b, m, deterministic) +} +func (m *GetEventsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetEventsRequest.Merge(m, src) +} +func (m *GetEventsRequest) XXX_Size() int { + return xxx_messageInfo_GetEventsRequest.Size(m) +} +func (m *GetEventsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetEventsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetEventsRequest proto.InternalMessageInfo + type ConditionParams struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Condition string `protobuf:"bytes,1,opt,name=condition,proto3" json:"condition,omitempty"` - Price float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"` - CheckBids bool `protobuf:"varint,3,opt,name=check_bids,json=checkBids,proto3" json:"check_bids,omitempty"` - CheckBidsAndAsks bool `protobuf:"varint,4,opt,name=check_bids_and_asks,json=checkBidsAndAsks,proto3" json:"check_bids_and_asks,omitempty"` - OrderbookAmount float64 `protobuf:"fixed64,5,opt,name=orderbook_amount,json=orderbookAmount,proto3" json:"orderbook_amount,omitempty"` + Condition string `protobuf:"bytes,1,opt,name=condition,proto3" json:"condition,omitempty"` + Price float64 `protobuf:"fixed64,2,opt,name=price,proto3" json:"price,omitempty"` + CheckBids bool `protobuf:"varint,3,opt,name=check_bids,json=checkBids,proto3" json:"check_bids,omitempty"` + CheckBidsAndAsks bool `protobuf:"varint,4,opt,name=check_bids_and_asks,json=checkBidsAndAsks,proto3" json:"check_bids_and_asks,omitempty"` + OrderbookAmount float64 `protobuf:"fixed64,5,opt,name=orderbook_amount,json=orderbookAmount,proto3" json:"orderbook_amount,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *ConditionParams) Reset() { - *x = ConditionParams{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[75] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ConditionParams) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConditionParams) ProtoMessage() {} - -func (x *ConditionParams) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[75] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConditionParams.ProtoReflect.Descriptor instead. +func (m *ConditionParams) Reset() { *m = ConditionParams{} } +func (m *ConditionParams) String() string { return proto.CompactTextString(m) } +func (*ConditionParams) ProtoMessage() {} func (*ConditionParams) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{75} + return fileDescriptor_77a6da22d6a3feb1, []int{70} } -func (x *ConditionParams) GetCondition() string { - if x != nil { - return x.Condition +func (m *ConditionParams) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ConditionParams.Unmarshal(m, b) +} +func (m *ConditionParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ConditionParams.Marshal(b, m, deterministic) +} +func (m *ConditionParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConditionParams.Merge(m, src) +} +func (m *ConditionParams) XXX_Size() int { + return xxx_messageInfo_ConditionParams.Size(m) +} +func (m *ConditionParams) XXX_DiscardUnknown() { + xxx_messageInfo_ConditionParams.DiscardUnknown(m) +} + +var xxx_messageInfo_ConditionParams proto.InternalMessageInfo + +func (m *ConditionParams) GetCondition() string { + if m != nil { + return m.Condition } return "" } -func (x *ConditionParams) GetPrice() float64 { - if x != nil { - return x.Price +func (m *ConditionParams) GetPrice() float64 { + if m != nil { + return m.Price } return 0 } -func (x *ConditionParams) GetCheckBids() bool { - if x != nil { - return x.CheckBids +func (m *ConditionParams) GetCheckBids() bool { + if m != nil { + return m.CheckBids } return false } -func (x *ConditionParams) GetCheckBidsAndAsks() bool { - if x != nil { - return x.CheckBidsAndAsks +func (m *ConditionParams) GetCheckBidsAndAsks() bool { + if m != nil { + return m.CheckBidsAndAsks } return false } -func (x *ConditionParams) GetOrderbookAmount() float64 { - if x != nil { - return x.OrderbookAmount +func (m *ConditionParams) GetOrderbookAmount() float64 { + if m != nil { + return m.OrderbookAmount } return 0 } type GetEventsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Exchange string `protobuf:"bytes,2,opt,name=exchange,proto3" json:"exchange,omitempty"` - Item string `protobuf:"bytes,3,opt,name=item,proto3" json:"item,omitempty"` - ConditionParams *ConditionParams `protobuf:"bytes,4,opt,name=condition_params,json=conditionParams,proto3" json:"condition_params,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,5,opt,name=pair,proto3" json:"pair,omitempty"` - Action string `protobuf:"bytes,6,opt,name=action,proto3" json:"action,omitempty"` - Executed bool `protobuf:"varint,7,opt,name=executed,proto3" json:"executed,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Exchange string `protobuf:"bytes,2,opt,name=exchange,proto3" json:"exchange,omitempty"` + Item string `protobuf:"bytes,3,opt,name=item,proto3" json:"item,omitempty"` + ConditionParams *ConditionParams `protobuf:"bytes,4,opt,name=condition_params,json=conditionParams,proto3" json:"condition_params,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,5,opt,name=pair,proto3" json:"pair,omitempty"` + Action string `protobuf:"bytes,6,opt,name=action,proto3" json:"action,omitempty"` + Executed bool `protobuf:"varint,7,opt,name=executed,proto3" json:"executed,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetEventsResponse) Reset() { - *x = GetEventsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[76] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetEventsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetEventsResponse) ProtoMessage() {} - -func (x *GetEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[76] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetEventsResponse.ProtoReflect.Descriptor instead. +func (m *GetEventsResponse) Reset() { *m = GetEventsResponse{} } +func (m *GetEventsResponse) String() string { return proto.CompactTextString(m) } +func (*GetEventsResponse) ProtoMessage() {} func (*GetEventsResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{76} + return fileDescriptor_77a6da22d6a3feb1, []int{71} } -func (x *GetEventsResponse) GetId() int64 { - if x != nil { - return x.Id +func (m *GetEventsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetEventsResponse.Unmarshal(m, b) +} +func (m *GetEventsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetEventsResponse.Marshal(b, m, deterministic) +} +func (m *GetEventsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetEventsResponse.Merge(m, src) +} +func (m *GetEventsResponse) XXX_Size() int { + return xxx_messageInfo_GetEventsResponse.Size(m) +} +func (m *GetEventsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetEventsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetEventsResponse proto.InternalMessageInfo + +func (m *GetEventsResponse) GetId() int64 { + if m != nil { + return m.Id } return 0 } -func (x *GetEventsResponse) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetEventsResponse) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetEventsResponse) GetItem() string { - if x != nil { - return x.Item +func (m *GetEventsResponse) GetItem() string { + if m != nil { + return m.Item } return "" } -func (x *GetEventsResponse) GetConditionParams() *ConditionParams { - if x != nil { - return x.ConditionParams +func (m *GetEventsResponse) GetConditionParams() *ConditionParams { + if m != nil { + return m.ConditionParams } return nil } -func (x *GetEventsResponse) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *GetEventsResponse) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *GetEventsResponse) GetAction() string { - if x != nil { - return x.Action +func (m *GetEventsResponse) GetAction() string { + if m != nil { + return m.Action } return "" } -func (x *GetEventsResponse) GetExecuted() bool { - if x != nil { - return x.Executed +func (m *GetEventsResponse) GetExecuted() bool { + if m != nil { + return m.Executed } return false } type AddEventRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Item string `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` - ConditionParams *ConditionParams `protobuf:"bytes,3,opt,name=condition_params,json=conditionParams,proto3" json:"condition_params,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,4,opt,name=pair,proto3" json:"pair,omitempty"` - AssetType string `protobuf:"bytes,5,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` - Action string `protobuf:"bytes,6,opt,name=action,proto3" json:"action,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Item string `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` + ConditionParams *ConditionParams `protobuf:"bytes,3,opt,name=condition_params,json=conditionParams,proto3" json:"condition_params,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,4,opt,name=pair,proto3" json:"pair,omitempty"` + AssetType string `protobuf:"bytes,5,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Action string `protobuf:"bytes,6,opt,name=action,proto3" json:"action,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *AddEventRequest) Reset() { - *x = AddEventRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[77] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AddEventRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddEventRequest) ProtoMessage() {} - -func (x *AddEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[77] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddEventRequest.ProtoReflect.Descriptor instead. +func (m *AddEventRequest) Reset() { *m = AddEventRequest{} } +func (m *AddEventRequest) String() string { return proto.CompactTextString(m) } +func (*AddEventRequest) ProtoMessage() {} func (*AddEventRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{77} + return fileDescriptor_77a6da22d6a3feb1, []int{72} } -func (x *AddEventRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *AddEventRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddEventRequest.Unmarshal(m, b) +} +func (m *AddEventRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddEventRequest.Marshal(b, m, deterministic) +} +func (m *AddEventRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddEventRequest.Merge(m, src) +} +func (m *AddEventRequest) XXX_Size() int { + return xxx_messageInfo_AddEventRequest.Size(m) +} +func (m *AddEventRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddEventRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddEventRequest proto.InternalMessageInfo + +func (m *AddEventRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *AddEventRequest) GetItem() string { - if x != nil { - return x.Item +func (m *AddEventRequest) GetItem() string { + if m != nil { + return m.Item } return "" } -func (x *AddEventRequest) GetConditionParams() *ConditionParams { - if x != nil { - return x.ConditionParams +func (m *AddEventRequest) GetConditionParams() *ConditionParams { + if m != nil { + return m.ConditionParams } return nil } -func (x *AddEventRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *AddEventRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *AddEventRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *AddEventRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } -func (x *AddEventRequest) GetAction() string { - if x != nil { - return x.Action +func (m *AddEventRequest) GetAction() string { + if m != nil { + return m.Action } return "" } type AddEventResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *AddEventResponse) Reset() { - *x = AddEventResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[78] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AddEventResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddEventResponse) ProtoMessage() {} - -func (x *AddEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[78] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddEventResponse.ProtoReflect.Descriptor instead. +func (m *AddEventResponse) Reset() { *m = AddEventResponse{} } +func (m *AddEventResponse) String() string { return proto.CompactTextString(m) } +func (*AddEventResponse) ProtoMessage() {} func (*AddEventResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{78} + return fileDescriptor_77a6da22d6a3feb1, []int{73} } -func (x *AddEventResponse) GetId() int64 { - if x != nil { - return x.Id +func (m *AddEventResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddEventResponse.Unmarshal(m, b) +} +func (m *AddEventResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddEventResponse.Marshal(b, m, deterministic) +} +func (m *AddEventResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddEventResponse.Merge(m, src) +} +func (m *AddEventResponse) XXX_Size() int { + return xxx_messageInfo_AddEventResponse.Size(m) +} +func (m *AddEventResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AddEventResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AddEventResponse proto.InternalMessageInfo + +func (m *AddEventResponse) GetId() int64 { + if m != nil { + return m.Id } return 0 } type RemoveEventRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *RemoveEventRequest) Reset() { - *x = RemoveEventRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[79] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RemoveEventRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveEventRequest) ProtoMessage() {} - -func (x *RemoveEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[79] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveEventRequest.ProtoReflect.Descriptor instead. +func (m *RemoveEventRequest) Reset() { *m = RemoveEventRequest{} } +func (m *RemoveEventRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveEventRequest) ProtoMessage() {} func (*RemoveEventRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{79} + return fileDescriptor_77a6da22d6a3feb1, []int{74} } -func (x *RemoveEventRequest) GetId() int64 { - if x != nil { - return x.Id +func (m *RemoveEventRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RemoveEventRequest.Unmarshal(m, b) +} +func (m *RemoveEventRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RemoveEventRequest.Marshal(b, m, deterministic) +} +func (m *RemoveEventRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveEventRequest.Merge(m, src) +} +func (m *RemoveEventRequest) XXX_Size() int { + return xxx_messageInfo_RemoveEventRequest.Size(m) +} +func (m *RemoveEventRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveEventRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveEventRequest proto.InternalMessageInfo + +func (m *RemoveEventRequest) GetId() int64 { + if m != nil { + return m.Id } return 0 } -type RemoveEventResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *RemoveEventResponse) Reset() { - *x = RemoveEventResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[80] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RemoveEventResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveEventResponse) ProtoMessage() {} - -func (x *RemoveEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[80] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveEventResponse.ProtoReflect.Descriptor instead. -func (*RemoveEventResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{80} -} - type GetCryptocurrencyDepositAddressesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetCryptocurrencyDepositAddressesRequest) Reset() { - *x = GetCryptocurrencyDepositAddressesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[81] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *GetCryptocurrencyDepositAddressesRequest) Reset() { + *m = GetCryptocurrencyDepositAddressesRequest{} } - -func (x *GetCryptocurrencyDepositAddressesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCryptocurrencyDepositAddressesRequest) ProtoMessage() {} - -func (x *GetCryptocurrencyDepositAddressesRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[81] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCryptocurrencyDepositAddressesRequest.ProtoReflect.Descriptor instead. +func (m *GetCryptocurrencyDepositAddressesRequest) String() string { return proto.CompactTextString(m) } +func (*GetCryptocurrencyDepositAddressesRequest) ProtoMessage() {} func (*GetCryptocurrencyDepositAddressesRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{81} + return fileDescriptor_77a6da22d6a3feb1, []int{75} } -func (x *GetCryptocurrencyDepositAddressesRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetCryptocurrencyDepositAddressesRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCryptocurrencyDepositAddressesRequest.Unmarshal(m, b) +} +func (m *GetCryptocurrencyDepositAddressesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCryptocurrencyDepositAddressesRequest.Marshal(b, m, deterministic) +} +func (m *GetCryptocurrencyDepositAddressesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCryptocurrencyDepositAddressesRequest.Merge(m, src) +} +func (m *GetCryptocurrencyDepositAddressesRequest) XXX_Size() int { + return xxx_messageInfo_GetCryptocurrencyDepositAddressesRequest.Size(m) +} +func (m *GetCryptocurrencyDepositAddressesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetCryptocurrencyDepositAddressesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCryptocurrencyDepositAddressesRequest proto.InternalMessageInfo + +func (m *GetCryptocurrencyDepositAddressesRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } type GetCryptocurrencyDepositAddressesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Addresses map[string]string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Addresses map[string]string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetCryptocurrencyDepositAddressesResponse) Reset() { - *x = GetCryptocurrencyDepositAddressesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[82] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *GetCryptocurrencyDepositAddressesResponse) Reset() { + *m = GetCryptocurrencyDepositAddressesResponse{} } - -func (x *GetCryptocurrencyDepositAddressesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCryptocurrencyDepositAddressesResponse) ProtoMessage() {} - -func (x *GetCryptocurrencyDepositAddressesResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[82] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCryptocurrencyDepositAddressesResponse.ProtoReflect.Descriptor instead. +func (m *GetCryptocurrencyDepositAddressesResponse) String() string { return proto.CompactTextString(m) } +func (*GetCryptocurrencyDepositAddressesResponse) ProtoMessage() {} func (*GetCryptocurrencyDepositAddressesResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{82} + return fileDescriptor_77a6da22d6a3feb1, []int{76} } -func (x *GetCryptocurrencyDepositAddressesResponse) GetAddresses() map[string]string { - if x != nil { - return x.Addresses +func (m *GetCryptocurrencyDepositAddressesResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCryptocurrencyDepositAddressesResponse.Unmarshal(m, b) +} +func (m *GetCryptocurrencyDepositAddressesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCryptocurrencyDepositAddressesResponse.Marshal(b, m, deterministic) +} +func (m *GetCryptocurrencyDepositAddressesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCryptocurrencyDepositAddressesResponse.Merge(m, src) +} +func (m *GetCryptocurrencyDepositAddressesResponse) XXX_Size() int { + return xxx_messageInfo_GetCryptocurrencyDepositAddressesResponse.Size(m) +} +func (m *GetCryptocurrencyDepositAddressesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetCryptocurrencyDepositAddressesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCryptocurrencyDepositAddressesResponse proto.InternalMessageInfo + +func (m *GetCryptocurrencyDepositAddressesResponse) GetAddresses() map[string]string { + if m != nil { + return m.Addresses } return nil } type GetCryptocurrencyDepositAddressRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Cryptocurrency string `protobuf:"bytes,2,opt,name=cryptocurrency,proto3" json:"cryptocurrency,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Cryptocurrency string `protobuf:"bytes,2,opt,name=cryptocurrency,proto3" json:"cryptocurrency,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetCryptocurrencyDepositAddressRequest) Reset() { - *x = GetCryptocurrencyDepositAddressRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[83] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *GetCryptocurrencyDepositAddressRequest) Reset() { + *m = GetCryptocurrencyDepositAddressRequest{} } - -func (x *GetCryptocurrencyDepositAddressRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCryptocurrencyDepositAddressRequest) ProtoMessage() {} - -func (x *GetCryptocurrencyDepositAddressRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[83] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCryptocurrencyDepositAddressRequest.ProtoReflect.Descriptor instead. +func (m *GetCryptocurrencyDepositAddressRequest) String() string { return proto.CompactTextString(m) } +func (*GetCryptocurrencyDepositAddressRequest) ProtoMessage() {} func (*GetCryptocurrencyDepositAddressRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{83} + return fileDescriptor_77a6da22d6a3feb1, []int{77} } -func (x *GetCryptocurrencyDepositAddressRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetCryptocurrencyDepositAddressRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCryptocurrencyDepositAddressRequest.Unmarshal(m, b) +} +func (m *GetCryptocurrencyDepositAddressRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCryptocurrencyDepositAddressRequest.Marshal(b, m, deterministic) +} +func (m *GetCryptocurrencyDepositAddressRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCryptocurrencyDepositAddressRequest.Merge(m, src) +} +func (m *GetCryptocurrencyDepositAddressRequest) XXX_Size() int { + return xxx_messageInfo_GetCryptocurrencyDepositAddressRequest.Size(m) +} +func (m *GetCryptocurrencyDepositAddressRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetCryptocurrencyDepositAddressRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCryptocurrencyDepositAddressRequest proto.InternalMessageInfo + +func (m *GetCryptocurrencyDepositAddressRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetCryptocurrencyDepositAddressRequest) GetCryptocurrency() string { - if x != nil { - return x.Cryptocurrency +func (m *GetCryptocurrencyDepositAddressRequest) GetCryptocurrency() string { + if m != nil { + return m.Cryptocurrency } return "" } type GetCryptocurrencyDepositAddressResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetCryptocurrencyDepositAddressResponse) Reset() { - *x = GetCryptocurrencyDepositAddressResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[84] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *GetCryptocurrencyDepositAddressResponse) Reset() { + *m = GetCryptocurrencyDepositAddressResponse{} } - -func (x *GetCryptocurrencyDepositAddressResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCryptocurrencyDepositAddressResponse) ProtoMessage() {} - -func (x *GetCryptocurrencyDepositAddressResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[84] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCryptocurrencyDepositAddressResponse.ProtoReflect.Descriptor instead. +func (m *GetCryptocurrencyDepositAddressResponse) String() string { return proto.CompactTextString(m) } +func (*GetCryptocurrencyDepositAddressResponse) ProtoMessage() {} func (*GetCryptocurrencyDepositAddressResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{84} + return fileDescriptor_77a6da22d6a3feb1, []int{78} } -func (x *GetCryptocurrencyDepositAddressResponse) GetAddress() string { - if x != nil { - return x.Address +func (m *GetCryptocurrencyDepositAddressResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetCryptocurrencyDepositAddressResponse.Unmarshal(m, b) +} +func (m *GetCryptocurrencyDepositAddressResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetCryptocurrencyDepositAddressResponse.Marshal(b, m, deterministic) +} +func (m *GetCryptocurrencyDepositAddressResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetCryptocurrencyDepositAddressResponse.Merge(m, src) +} +func (m *GetCryptocurrencyDepositAddressResponse) XXX_Size() int { + return xxx_messageInfo_GetCryptocurrencyDepositAddressResponse.Size(m) +} +func (m *GetCryptocurrencyDepositAddressResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetCryptocurrencyDepositAddressResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetCryptocurrencyDepositAddressResponse proto.InternalMessageInfo + +func (m *GetCryptocurrencyDepositAddressResponse) GetAddress() string { + if m != nil { + return m.Address } return "" } type WithdrawFiatRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Currency string `protobuf:"bytes,2,opt,name=currency,proto3" json:"currency,omitempty"` - Amount float64 `protobuf:"fixed64,3,opt,name=amount,proto3" json:"amount,omitempty"` - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` - BankAccountId string `protobuf:"bytes,5,opt,name=bank_account_id,json=bankAccountId,proto3" json:"bank_account_id,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Currency string `protobuf:"bytes,2,opt,name=currency,proto3" json:"currency,omitempty"` + Amount float64 `protobuf:"fixed64,3,opt,name=amount,proto3" json:"amount,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + BankAccountId string `protobuf:"bytes,5,opt,name=bank_account_id,json=bankAccountId,proto3" json:"bank_account_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawFiatRequest) Reset() { - *x = WithdrawFiatRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[85] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawFiatRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawFiatRequest) ProtoMessage() {} - -func (x *WithdrawFiatRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[85] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawFiatRequest.ProtoReflect.Descriptor instead. +func (m *WithdrawFiatRequest) Reset() { *m = WithdrawFiatRequest{} } +func (m *WithdrawFiatRequest) String() string { return proto.CompactTextString(m) } +func (*WithdrawFiatRequest) ProtoMessage() {} func (*WithdrawFiatRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{85} + return fileDescriptor_77a6da22d6a3feb1, []int{79} } -func (x *WithdrawFiatRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *WithdrawFiatRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawFiatRequest.Unmarshal(m, b) +} +func (m *WithdrawFiatRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawFiatRequest.Marshal(b, m, deterministic) +} +func (m *WithdrawFiatRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawFiatRequest.Merge(m, src) +} +func (m *WithdrawFiatRequest) XXX_Size() int { + return xxx_messageInfo_WithdrawFiatRequest.Size(m) +} +func (m *WithdrawFiatRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawFiatRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawFiatRequest proto.InternalMessageInfo + +func (m *WithdrawFiatRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *WithdrawFiatRequest) GetCurrency() string { - if x != nil { - return x.Currency +func (m *WithdrawFiatRequest) GetCurrency() string { + if m != nil { + return m.Currency } return "" } -func (x *WithdrawFiatRequest) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *WithdrawFiatRequest) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *WithdrawFiatRequest) GetDescription() string { - if x != nil { - return x.Description +func (m *WithdrawFiatRequest) GetDescription() string { + if m != nil { + return m.Description } return "" } -func (x *WithdrawFiatRequest) GetBankAccountId() string { - if x != nil { - return x.BankAccountId +func (m *WithdrawFiatRequest) GetBankAccountId() string { + if m != nil { + return m.BankAccountId } return "" } type WithdrawCryptoRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` - AddressTag string `protobuf:"bytes,3,opt,name=address_tag,json=addressTag,proto3" json:"address_tag,omitempty"` - Currency string `protobuf:"bytes,4,opt,name=currency,proto3" json:"currency,omitempty"` - Amount float64 `protobuf:"fixed64,5,opt,name=amount,proto3" json:"amount,omitempty"` - Fee float64 `protobuf:"fixed64,6,opt,name=fee,proto3" json:"fee,omitempty"` - Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + AddressTag string `protobuf:"bytes,3,opt,name=address_tag,json=addressTag,proto3" json:"address_tag,omitempty"` + Currency string `protobuf:"bytes,4,opt,name=currency,proto3" json:"currency,omitempty"` + Amount float64 `protobuf:"fixed64,5,opt,name=amount,proto3" json:"amount,omitempty"` + Fee float64 `protobuf:"fixed64,6,opt,name=fee,proto3" json:"fee,omitempty"` + Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawCryptoRequest) Reset() { - *x = WithdrawCryptoRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[86] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawCryptoRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawCryptoRequest) ProtoMessage() {} - -func (x *WithdrawCryptoRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[86] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawCryptoRequest.ProtoReflect.Descriptor instead. +func (m *WithdrawCryptoRequest) Reset() { *m = WithdrawCryptoRequest{} } +func (m *WithdrawCryptoRequest) String() string { return proto.CompactTextString(m) } +func (*WithdrawCryptoRequest) ProtoMessage() {} func (*WithdrawCryptoRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{86} + return fileDescriptor_77a6da22d6a3feb1, []int{80} } -func (x *WithdrawCryptoRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *WithdrawCryptoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawCryptoRequest.Unmarshal(m, b) +} +func (m *WithdrawCryptoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawCryptoRequest.Marshal(b, m, deterministic) +} +func (m *WithdrawCryptoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawCryptoRequest.Merge(m, src) +} +func (m *WithdrawCryptoRequest) XXX_Size() int { + return xxx_messageInfo_WithdrawCryptoRequest.Size(m) +} +func (m *WithdrawCryptoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawCryptoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawCryptoRequest proto.InternalMessageInfo + +func (m *WithdrawCryptoRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *WithdrawCryptoRequest) GetAddress() string { - if x != nil { - return x.Address +func (m *WithdrawCryptoRequest) GetAddress() string { + if m != nil { + return m.Address } return "" } -func (x *WithdrawCryptoRequest) GetAddressTag() string { - if x != nil { - return x.AddressTag +func (m *WithdrawCryptoRequest) GetAddressTag() string { + if m != nil { + return m.AddressTag } return "" } -func (x *WithdrawCryptoRequest) GetCurrency() string { - if x != nil { - return x.Currency +func (m *WithdrawCryptoRequest) GetCurrency() string { + if m != nil { + return m.Currency } return "" } -func (x *WithdrawCryptoRequest) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *WithdrawCryptoRequest) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *WithdrawCryptoRequest) GetFee() float64 { - if x != nil { - return x.Fee +func (m *WithdrawCryptoRequest) GetFee() float64 { + if m != nil { + return m.Fee } return 0 } -func (x *WithdrawCryptoRequest) GetDescription() string { - if x != nil { - return x.Description +func (m *WithdrawCryptoRequest) GetDescription() string { + if m != nil { + return m.Description } return "" } type WithdrawResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawResponse) Reset() { - *x = WithdrawResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[87] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawResponse) ProtoMessage() {} - -func (x *WithdrawResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[87] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawResponse.ProtoReflect.Descriptor instead. +func (m *WithdrawResponse) Reset() { *m = WithdrawResponse{} } +func (m *WithdrawResponse) String() string { return proto.CompactTextString(m) } +func (*WithdrawResponse) ProtoMessage() {} func (*WithdrawResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{87} + return fileDescriptor_77a6da22d6a3feb1, []int{81} } -func (x *WithdrawResponse) GetId() string { - if x != nil { - return x.Id +func (m *WithdrawResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawResponse.Unmarshal(m, b) +} +func (m *WithdrawResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawResponse.Marshal(b, m, deterministic) +} +func (m *WithdrawResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawResponse.Merge(m, src) +} +func (m *WithdrawResponse) XXX_Size() int { + return xxx_messageInfo_WithdrawResponse.Size(m) +} +func (m *WithdrawResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawResponse proto.InternalMessageInfo + +func (m *WithdrawResponse) GetId() string { + if m != nil { + return m.Id } return "" } -func (x *WithdrawResponse) GetStatus() string { - if x != nil { - return x.Status +func (m *WithdrawResponse) GetStatus() string { + if m != nil { + return m.Status } return "" } type WithdrawalEventByIDRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawalEventByIDRequest) Reset() { - *x = WithdrawalEventByIDRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[88] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawalEventByIDRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawalEventByIDRequest) ProtoMessage() {} - -func (x *WithdrawalEventByIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[88] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawalEventByIDRequest.ProtoReflect.Descriptor instead. +func (m *WithdrawalEventByIDRequest) Reset() { *m = WithdrawalEventByIDRequest{} } +func (m *WithdrawalEventByIDRequest) String() string { return proto.CompactTextString(m) } +func (*WithdrawalEventByIDRequest) ProtoMessage() {} func (*WithdrawalEventByIDRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{88} + return fileDescriptor_77a6da22d6a3feb1, []int{82} } -func (x *WithdrawalEventByIDRequest) GetId() string { - if x != nil { - return x.Id +func (m *WithdrawalEventByIDRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawalEventByIDRequest.Unmarshal(m, b) +} +func (m *WithdrawalEventByIDRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawalEventByIDRequest.Marshal(b, m, deterministic) +} +func (m *WithdrawalEventByIDRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawalEventByIDRequest.Merge(m, src) +} +func (m *WithdrawalEventByIDRequest) XXX_Size() int { + return xxx_messageInfo_WithdrawalEventByIDRequest.Size(m) +} +func (m *WithdrawalEventByIDRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawalEventByIDRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawalEventByIDRequest proto.InternalMessageInfo + +func (m *WithdrawalEventByIDRequest) GetId() string { + if m != nil { + return m.Id } return "" } type WithdrawalEventByIDResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Event *WithdrawalEventResponse `protobuf:"bytes,2,opt,name=event,proto3" json:"event,omitempty"` + Event *WithdrawalEventResponse `protobuf:"bytes,2,opt,name=event,proto3" json:"event,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawalEventByIDResponse) Reset() { - *x = WithdrawalEventByIDResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[89] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawalEventByIDResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawalEventByIDResponse) ProtoMessage() {} - -func (x *WithdrawalEventByIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[89] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawalEventByIDResponse.ProtoReflect.Descriptor instead. +func (m *WithdrawalEventByIDResponse) Reset() { *m = WithdrawalEventByIDResponse{} } +func (m *WithdrawalEventByIDResponse) String() string { return proto.CompactTextString(m) } +func (*WithdrawalEventByIDResponse) ProtoMessage() {} func (*WithdrawalEventByIDResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{89} + return fileDescriptor_77a6da22d6a3feb1, []int{83} } -func (x *WithdrawalEventByIDResponse) GetEvent() *WithdrawalEventResponse { - if x != nil { - return x.Event +func (m *WithdrawalEventByIDResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawalEventByIDResponse.Unmarshal(m, b) +} +func (m *WithdrawalEventByIDResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawalEventByIDResponse.Marshal(b, m, deterministic) +} +func (m *WithdrawalEventByIDResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawalEventByIDResponse.Merge(m, src) +} +func (m *WithdrawalEventByIDResponse) XXX_Size() int { + return xxx_messageInfo_WithdrawalEventByIDResponse.Size(m) +} +func (m *WithdrawalEventByIDResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawalEventByIDResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawalEventByIDResponse proto.InternalMessageInfo + +func (m *WithdrawalEventByIDResponse) GetEvent() *WithdrawalEventResponse { + if m != nil { + return m.Event } return nil } type WithdrawalEventsByExchangeRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Limit int32 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Limit int32 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawalEventsByExchangeRequest) Reset() { - *x = WithdrawalEventsByExchangeRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[90] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawalEventsByExchangeRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawalEventsByExchangeRequest) ProtoMessage() {} - -func (x *WithdrawalEventsByExchangeRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[90] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawalEventsByExchangeRequest.ProtoReflect.Descriptor instead. +func (m *WithdrawalEventsByExchangeRequest) Reset() { *m = WithdrawalEventsByExchangeRequest{} } +func (m *WithdrawalEventsByExchangeRequest) String() string { return proto.CompactTextString(m) } +func (*WithdrawalEventsByExchangeRequest) ProtoMessage() {} func (*WithdrawalEventsByExchangeRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{90} + return fileDescriptor_77a6da22d6a3feb1, []int{84} } -func (x *WithdrawalEventsByExchangeRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *WithdrawalEventsByExchangeRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawalEventsByExchangeRequest.Unmarshal(m, b) +} +func (m *WithdrawalEventsByExchangeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawalEventsByExchangeRequest.Marshal(b, m, deterministic) +} +func (m *WithdrawalEventsByExchangeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawalEventsByExchangeRequest.Merge(m, src) +} +func (m *WithdrawalEventsByExchangeRequest) XXX_Size() int { + return xxx_messageInfo_WithdrawalEventsByExchangeRequest.Size(m) +} +func (m *WithdrawalEventsByExchangeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawalEventsByExchangeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawalEventsByExchangeRequest proto.InternalMessageInfo + +func (m *WithdrawalEventsByExchangeRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *WithdrawalEventsByExchangeRequest) GetId() string { - if x != nil { - return x.Id +func (m *WithdrawalEventsByExchangeRequest) GetId() string { + if m != nil { + return m.Id } return "" } -func (x *WithdrawalEventsByExchangeRequest) GetLimit() int32 { - if x != nil { - return x.Limit +func (m *WithdrawalEventsByExchangeRequest) GetLimit() int32 { + if m != nil { + return m.Limit } return 0 } type WithdrawalEventsByDateRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Start string `protobuf:"bytes,2,opt,name=start,proto3" json:"start,omitempty"` - End string `protobuf:"bytes,3,opt,name=end,proto3" json:"end,omitempty"` - Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Start string `protobuf:"bytes,2,opt,name=start,proto3" json:"start,omitempty"` + End string `protobuf:"bytes,3,opt,name=end,proto3" json:"end,omitempty"` + Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawalEventsByDateRequest) Reset() { - *x = WithdrawalEventsByDateRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[91] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawalEventsByDateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawalEventsByDateRequest) ProtoMessage() {} - -func (x *WithdrawalEventsByDateRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[91] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawalEventsByDateRequest.ProtoReflect.Descriptor instead. +func (m *WithdrawalEventsByDateRequest) Reset() { *m = WithdrawalEventsByDateRequest{} } +func (m *WithdrawalEventsByDateRequest) String() string { return proto.CompactTextString(m) } +func (*WithdrawalEventsByDateRequest) ProtoMessage() {} func (*WithdrawalEventsByDateRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{91} + return fileDescriptor_77a6da22d6a3feb1, []int{85} } -func (x *WithdrawalEventsByDateRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *WithdrawalEventsByDateRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawalEventsByDateRequest.Unmarshal(m, b) +} +func (m *WithdrawalEventsByDateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawalEventsByDateRequest.Marshal(b, m, deterministic) +} +func (m *WithdrawalEventsByDateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawalEventsByDateRequest.Merge(m, src) +} +func (m *WithdrawalEventsByDateRequest) XXX_Size() int { + return xxx_messageInfo_WithdrawalEventsByDateRequest.Size(m) +} +func (m *WithdrawalEventsByDateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawalEventsByDateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawalEventsByDateRequest proto.InternalMessageInfo + +func (m *WithdrawalEventsByDateRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *WithdrawalEventsByDateRequest) GetStart() string { - if x != nil { - return x.Start +func (m *WithdrawalEventsByDateRequest) GetStart() string { + if m != nil { + return m.Start } return "" } -func (x *WithdrawalEventsByDateRequest) GetEnd() string { - if x != nil { - return x.End +func (m *WithdrawalEventsByDateRequest) GetEnd() string { + if m != nil { + return m.End } return "" } -func (x *WithdrawalEventsByDateRequest) GetLimit() int32 { - if x != nil { - return x.Limit +func (m *WithdrawalEventsByDateRequest) GetLimit() int32 { + if m != nil { + return m.Limit } return 0 } type WithdrawalEventsByExchangeResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Event []*WithdrawalEventResponse `protobuf:"bytes,2,rep,name=event,proto3" json:"event,omitempty"` + Event []*WithdrawalEventResponse `protobuf:"bytes,2,rep,name=event,proto3" json:"event,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawalEventsByExchangeResponse) Reset() { - *x = WithdrawalEventsByExchangeResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[92] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawalEventsByExchangeResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawalEventsByExchangeResponse) ProtoMessage() {} - -func (x *WithdrawalEventsByExchangeResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[92] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawalEventsByExchangeResponse.ProtoReflect.Descriptor instead. +func (m *WithdrawalEventsByExchangeResponse) Reset() { *m = WithdrawalEventsByExchangeResponse{} } +func (m *WithdrawalEventsByExchangeResponse) String() string { return proto.CompactTextString(m) } +func (*WithdrawalEventsByExchangeResponse) ProtoMessage() {} func (*WithdrawalEventsByExchangeResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{92} + return fileDescriptor_77a6da22d6a3feb1, []int{86} } -func (x *WithdrawalEventsByExchangeResponse) GetEvent() []*WithdrawalEventResponse { - if x != nil { - return x.Event +func (m *WithdrawalEventsByExchangeResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawalEventsByExchangeResponse.Unmarshal(m, b) +} +func (m *WithdrawalEventsByExchangeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawalEventsByExchangeResponse.Marshal(b, m, deterministic) +} +func (m *WithdrawalEventsByExchangeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawalEventsByExchangeResponse.Merge(m, src) +} +func (m *WithdrawalEventsByExchangeResponse) XXX_Size() int { + return xxx_messageInfo_WithdrawalEventsByExchangeResponse.Size(m) +} +func (m *WithdrawalEventsByExchangeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawalEventsByExchangeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawalEventsByExchangeResponse proto.InternalMessageInfo + +func (m *WithdrawalEventsByExchangeResponse) GetEvent() []*WithdrawalEventResponse { + if m != nil { + return m.Event } return nil } type WithdrawalEventResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Exchange *WithdrawlExchangeEvent `protobuf:"bytes,3,opt,name=exchange,proto3" json:"exchange,omitempty"` - Request *WithdrawalRequestEvent `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"` - CreatedAt *timestamp.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - UpdatedAt *timestamp.Timestamp `protobuf:"bytes,6,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Exchange *WithdrawlExchangeEvent `protobuf:"bytes,3,opt,name=exchange,proto3" json:"exchange,omitempty"` + Request *WithdrawalRequestEvent `protobuf:"bytes,4,opt,name=request,proto3" json:"request,omitempty"` + CreatedAt *timestamp.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + UpdatedAt *timestamp.Timestamp `protobuf:"bytes,6,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawalEventResponse) Reset() { - *x = WithdrawalEventResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[93] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawalEventResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawalEventResponse) ProtoMessage() {} - -func (x *WithdrawalEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[93] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawalEventResponse.ProtoReflect.Descriptor instead. +func (m *WithdrawalEventResponse) Reset() { *m = WithdrawalEventResponse{} } +func (m *WithdrawalEventResponse) String() string { return proto.CompactTextString(m) } +func (*WithdrawalEventResponse) ProtoMessage() {} func (*WithdrawalEventResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{93} + return fileDescriptor_77a6da22d6a3feb1, []int{87} } -func (x *WithdrawalEventResponse) GetId() string { - if x != nil { - return x.Id +func (m *WithdrawalEventResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawalEventResponse.Unmarshal(m, b) +} +func (m *WithdrawalEventResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawalEventResponse.Marshal(b, m, deterministic) +} +func (m *WithdrawalEventResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawalEventResponse.Merge(m, src) +} +func (m *WithdrawalEventResponse) XXX_Size() int { + return xxx_messageInfo_WithdrawalEventResponse.Size(m) +} +func (m *WithdrawalEventResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawalEventResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawalEventResponse proto.InternalMessageInfo + +func (m *WithdrawalEventResponse) GetId() string { + if m != nil { + return m.Id } return "" } -func (x *WithdrawalEventResponse) GetExchange() *WithdrawlExchangeEvent { - if x != nil { - return x.Exchange +func (m *WithdrawalEventResponse) GetExchange() *WithdrawlExchangeEvent { + if m != nil { + return m.Exchange } return nil } -func (x *WithdrawalEventResponse) GetRequest() *WithdrawalRequestEvent { - if x != nil { - return x.Request +func (m *WithdrawalEventResponse) GetRequest() *WithdrawalRequestEvent { + if m != nil { + return m.Request } return nil } -func (x *WithdrawalEventResponse) GetCreatedAt() *timestamp.Timestamp { - if x != nil { - return x.CreatedAt +func (m *WithdrawalEventResponse) GetCreatedAt() *timestamp.Timestamp { + if m != nil { + return m.CreatedAt } return nil } -func (x *WithdrawalEventResponse) GetUpdatedAt() *timestamp.Timestamp { - if x != nil { - return x.UpdatedAt +func (m *WithdrawalEventResponse) GetUpdatedAt() *timestamp.Timestamp { + if m != nil { + return m.UpdatedAt } return nil } type WithdrawlExchangeEvent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` - Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawlExchangeEvent) Reset() { - *x = WithdrawlExchangeEvent{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[94] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawlExchangeEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawlExchangeEvent) ProtoMessage() {} - -func (x *WithdrawlExchangeEvent) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[94] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawlExchangeEvent.ProtoReflect.Descriptor instead. +func (m *WithdrawlExchangeEvent) Reset() { *m = WithdrawlExchangeEvent{} } +func (m *WithdrawlExchangeEvent) String() string { return proto.CompactTextString(m) } +func (*WithdrawlExchangeEvent) ProtoMessage() {} func (*WithdrawlExchangeEvent) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{94} + return fileDescriptor_77a6da22d6a3feb1, []int{88} } -func (x *WithdrawlExchangeEvent) GetName() string { - if x != nil { - return x.Name +func (m *WithdrawlExchangeEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawlExchangeEvent.Unmarshal(m, b) +} +func (m *WithdrawlExchangeEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawlExchangeEvent.Marshal(b, m, deterministic) +} +func (m *WithdrawlExchangeEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawlExchangeEvent.Merge(m, src) +} +func (m *WithdrawlExchangeEvent) XXX_Size() int { + return xxx_messageInfo_WithdrawlExchangeEvent.Size(m) +} +func (m *WithdrawlExchangeEvent) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawlExchangeEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawlExchangeEvent proto.InternalMessageInfo + +func (m *WithdrawlExchangeEvent) GetName() string { + if m != nil { + return m.Name } return "" } -func (x *WithdrawlExchangeEvent) GetId() string { - if x != nil { - return x.Id +func (m *WithdrawlExchangeEvent) GetId() string { + if m != nil { + return m.Id } return "" } -func (x *WithdrawlExchangeEvent) GetStatus() string { - if x != nil { - return x.Status +func (m *WithdrawlExchangeEvent) GetStatus() string { + if m != nil { + return m.Status } return "" } type WithdrawalRequestEvent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Currency string `protobuf:"bytes,2,opt,name=currency,proto3" json:"currency,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Amount float64 `protobuf:"fixed64,4,opt,name=amount,proto3" json:"amount,omitempty"` - Type int32 `protobuf:"varint,5,opt,name=type,proto3" json:"type,omitempty"` - Fiat *FiatWithdrawalEvent `protobuf:"bytes,6,opt,name=fiat,proto3" json:"fiat,omitempty"` - Crypto *CryptoWithdrawalEvent `protobuf:"bytes,7,opt,name=crypto,proto3" json:"crypto,omitempty"` + Currency string `protobuf:"bytes,2,opt,name=currency,proto3" json:"currency,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Amount float64 `protobuf:"fixed64,4,opt,name=amount,proto3" json:"amount,omitempty"` + Type int32 `protobuf:"varint,5,opt,name=type,proto3" json:"type,omitempty"` + Fiat *FiatWithdrawalEvent `protobuf:"bytes,6,opt,name=fiat,proto3" json:"fiat,omitempty"` + Crypto *CryptoWithdrawalEvent `protobuf:"bytes,7,opt,name=crypto,proto3" json:"crypto,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *WithdrawalRequestEvent) Reset() { - *x = WithdrawalRequestEvent{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[95] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WithdrawalRequestEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WithdrawalRequestEvent) ProtoMessage() {} - -func (x *WithdrawalRequestEvent) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[95] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WithdrawalRequestEvent.ProtoReflect.Descriptor instead. +func (m *WithdrawalRequestEvent) Reset() { *m = WithdrawalRequestEvent{} } +func (m *WithdrawalRequestEvent) String() string { return proto.CompactTextString(m) } +func (*WithdrawalRequestEvent) ProtoMessage() {} func (*WithdrawalRequestEvent) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{95} + return fileDescriptor_77a6da22d6a3feb1, []int{89} } -func (x *WithdrawalRequestEvent) GetCurrency() string { - if x != nil { - return x.Currency +func (m *WithdrawalRequestEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WithdrawalRequestEvent.Unmarshal(m, b) +} +func (m *WithdrawalRequestEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WithdrawalRequestEvent.Marshal(b, m, deterministic) +} +func (m *WithdrawalRequestEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_WithdrawalRequestEvent.Merge(m, src) +} +func (m *WithdrawalRequestEvent) XXX_Size() int { + return xxx_messageInfo_WithdrawalRequestEvent.Size(m) +} +func (m *WithdrawalRequestEvent) XXX_DiscardUnknown() { + xxx_messageInfo_WithdrawalRequestEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_WithdrawalRequestEvent proto.InternalMessageInfo + +func (m *WithdrawalRequestEvent) GetCurrency() string { + if m != nil { + return m.Currency } return "" } -func (x *WithdrawalRequestEvent) GetDescription() string { - if x != nil { - return x.Description +func (m *WithdrawalRequestEvent) GetDescription() string { + if m != nil { + return m.Description } return "" } -func (x *WithdrawalRequestEvent) GetAmount() float64 { - if x != nil { - return x.Amount +func (m *WithdrawalRequestEvent) GetAmount() float64 { + if m != nil { + return m.Amount } return 0 } -func (x *WithdrawalRequestEvent) GetType() int32 { - if x != nil { - return x.Type +func (m *WithdrawalRequestEvent) GetType() int32 { + if m != nil { + return m.Type } return 0 } -func (x *WithdrawalRequestEvent) GetFiat() *FiatWithdrawalEvent { - if x != nil { - return x.Fiat +func (m *WithdrawalRequestEvent) GetFiat() *FiatWithdrawalEvent { + if m != nil { + return m.Fiat } return nil } -func (x *WithdrawalRequestEvent) GetCrypto() *CryptoWithdrawalEvent { - if x != nil { - return x.Crypto +func (m *WithdrawalRequestEvent) GetCrypto() *CryptoWithdrawalEvent { + if m != nil { + return m.Crypto } return nil } type FiatWithdrawalEvent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BankName string `protobuf:"bytes,1,opt,name=bank_name,json=bankName,proto3" json:"bank_name,omitempty"` - AccountName string `protobuf:"bytes,2,opt,name=account_name,json=accountName,proto3" json:"account_name,omitempty"` - AccountNumber string `protobuf:"bytes,3,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` - Bsb string `protobuf:"bytes,4,opt,name=bsb,proto3" json:"bsb,omitempty"` - Swift string `protobuf:"bytes,5,opt,name=swift,proto3" json:"swift,omitempty"` - Iban string `protobuf:"bytes,6,opt,name=iban,proto3" json:"iban,omitempty"` + BankName string `protobuf:"bytes,1,opt,name=bank_name,json=bankName,proto3" json:"bank_name,omitempty"` + AccountName string `protobuf:"bytes,2,opt,name=account_name,json=accountName,proto3" json:"account_name,omitempty"` + AccountNumber string `protobuf:"bytes,3,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + Bsb string `protobuf:"bytes,4,opt,name=bsb,proto3" json:"bsb,omitempty"` + Swift string `protobuf:"bytes,5,opt,name=swift,proto3" json:"swift,omitempty"` + Iban string `protobuf:"bytes,6,opt,name=iban,proto3" json:"iban,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *FiatWithdrawalEvent) Reset() { - *x = FiatWithdrawalEvent{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[96] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FiatWithdrawalEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FiatWithdrawalEvent) ProtoMessage() {} - -func (x *FiatWithdrawalEvent) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[96] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FiatWithdrawalEvent.ProtoReflect.Descriptor instead. +func (m *FiatWithdrawalEvent) Reset() { *m = FiatWithdrawalEvent{} } +func (m *FiatWithdrawalEvent) String() string { return proto.CompactTextString(m) } +func (*FiatWithdrawalEvent) ProtoMessage() {} func (*FiatWithdrawalEvent) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{96} + return fileDescriptor_77a6da22d6a3feb1, []int{90} } -func (x *FiatWithdrawalEvent) GetBankName() string { - if x != nil { - return x.BankName +func (m *FiatWithdrawalEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_FiatWithdrawalEvent.Unmarshal(m, b) +} +func (m *FiatWithdrawalEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_FiatWithdrawalEvent.Marshal(b, m, deterministic) +} +func (m *FiatWithdrawalEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_FiatWithdrawalEvent.Merge(m, src) +} +func (m *FiatWithdrawalEvent) XXX_Size() int { + return xxx_messageInfo_FiatWithdrawalEvent.Size(m) +} +func (m *FiatWithdrawalEvent) XXX_DiscardUnknown() { + xxx_messageInfo_FiatWithdrawalEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_FiatWithdrawalEvent proto.InternalMessageInfo + +func (m *FiatWithdrawalEvent) GetBankName() string { + if m != nil { + return m.BankName } return "" } -func (x *FiatWithdrawalEvent) GetAccountName() string { - if x != nil { - return x.AccountName +func (m *FiatWithdrawalEvent) GetAccountName() string { + if m != nil { + return m.AccountName } return "" } -func (x *FiatWithdrawalEvent) GetAccountNumber() string { - if x != nil { - return x.AccountNumber +func (m *FiatWithdrawalEvent) GetAccountNumber() string { + if m != nil { + return m.AccountNumber } return "" } -func (x *FiatWithdrawalEvent) GetBsb() string { - if x != nil { - return x.Bsb +func (m *FiatWithdrawalEvent) GetBsb() string { + if m != nil { + return m.Bsb } return "" } -func (x *FiatWithdrawalEvent) GetSwift() string { - if x != nil { - return x.Swift +func (m *FiatWithdrawalEvent) GetSwift() string { + if m != nil { + return m.Swift } return "" } -func (x *FiatWithdrawalEvent) GetIban() string { - if x != nil { - return x.Iban +func (m *FiatWithdrawalEvent) GetIban() string { + if m != nil { + return m.Iban } return "" } type CryptoWithdrawalEvent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - AddressTag string `protobuf:"bytes,2,opt,name=address_tag,json=addressTag,proto3" json:"address_tag,omitempty"` - Fee float64 `protobuf:"fixed64,3,opt,name=fee,proto3" json:"fee,omitempty"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + AddressTag string `protobuf:"bytes,2,opt,name=address_tag,json=addressTag,proto3" json:"address_tag,omitempty"` + Fee float64 `protobuf:"fixed64,3,opt,name=fee,proto3" json:"fee,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *CryptoWithdrawalEvent) Reset() { - *x = CryptoWithdrawalEvent{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[97] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CryptoWithdrawalEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CryptoWithdrawalEvent) ProtoMessage() {} - -func (x *CryptoWithdrawalEvent) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[97] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CryptoWithdrawalEvent.ProtoReflect.Descriptor instead. +func (m *CryptoWithdrawalEvent) Reset() { *m = CryptoWithdrawalEvent{} } +func (m *CryptoWithdrawalEvent) String() string { return proto.CompactTextString(m) } +func (*CryptoWithdrawalEvent) ProtoMessage() {} func (*CryptoWithdrawalEvent) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{97} + return fileDescriptor_77a6da22d6a3feb1, []int{91} } -func (x *CryptoWithdrawalEvent) GetAddress() string { - if x != nil { - return x.Address +func (m *CryptoWithdrawalEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CryptoWithdrawalEvent.Unmarshal(m, b) +} +func (m *CryptoWithdrawalEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CryptoWithdrawalEvent.Marshal(b, m, deterministic) +} +func (m *CryptoWithdrawalEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_CryptoWithdrawalEvent.Merge(m, src) +} +func (m *CryptoWithdrawalEvent) XXX_Size() int { + return xxx_messageInfo_CryptoWithdrawalEvent.Size(m) +} +func (m *CryptoWithdrawalEvent) XXX_DiscardUnknown() { + xxx_messageInfo_CryptoWithdrawalEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_CryptoWithdrawalEvent proto.InternalMessageInfo + +func (m *CryptoWithdrawalEvent) GetAddress() string { + if m != nil { + return m.Address } return "" } -func (x *CryptoWithdrawalEvent) GetAddressTag() string { - if x != nil { - return x.AddressTag +func (m *CryptoWithdrawalEvent) GetAddressTag() string { + if m != nil { + return m.AddressTag } return "" } -func (x *CryptoWithdrawalEvent) GetFee() float64 { - if x != nil { - return x.Fee +func (m *CryptoWithdrawalEvent) GetFee() float64 { + if m != nil { + return m.Fee } return 0 } type GetLoggerDetailsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Logger string `protobuf:"bytes,1,opt,name=logger,proto3" json:"logger,omitempty"` + Logger string `protobuf:"bytes,1,opt,name=logger,proto3" json:"logger,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetLoggerDetailsRequest) Reset() { - *x = GetLoggerDetailsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[98] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetLoggerDetailsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetLoggerDetailsRequest) ProtoMessage() {} - -func (x *GetLoggerDetailsRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[98] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetLoggerDetailsRequest.ProtoReflect.Descriptor instead. +func (m *GetLoggerDetailsRequest) Reset() { *m = GetLoggerDetailsRequest{} } +func (m *GetLoggerDetailsRequest) String() string { return proto.CompactTextString(m) } +func (*GetLoggerDetailsRequest) ProtoMessage() {} func (*GetLoggerDetailsRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{98} + return fileDescriptor_77a6da22d6a3feb1, []int{92} } -func (x *GetLoggerDetailsRequest) GetLogger() string { - if x != nil { - return x.Logger +func (m *GetLoggerDetailsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetLoggerDetailsRequest.Unmarshal(m, b) +} +func (m *GetLoggerDetailsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetLoggerDetailsRequest.Marshal(b, m, deterministic) +} +func (m *GetLoggerDetailsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetLoggerDetailsRequest.Merge(m, src) +} +func (m *GetLoggerDetailsRequest) XXX_Size() int { + return xxx_messageInfo_GetLoggerDetailsRequest.Size(m) +} +func (m *GetLoggerDetailsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetLoggerDetailsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetLoggerDetailsRequest proto.InternalMessageInfo + +func (m *GetLoggerDetailsRequest) GetLogger() string { + if m != nil { + return m.Logger } return "" } type GetLoggerDetailsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Info bool `protobuf:"varint,1,opt,name=info,proto3" json:"info,omitempty"` - Debug bool `protobuf:"varint,2,opt,name=debug,proto3" json:"debug,omitempty"` - Warn bool `protobuf:"varint,3,opt,name=warn,proto3" json:"warn,omitempty"` - Error bool `protobuf:"varint,4,opt,name=error,proto3" json:"error,omitempty"` + Info bool `protobuf:"varint,1,opt,name=info,proto3" json:"info,omitempty"` + Debug bool `protobuf:"varint,2,opt,name=debug,proto3" json:"debug,omitempty"` + Warn bool `protobuf:"varint,3,opt,name=warn,proto3" json:"warn,omitempty"` + Error bool `protobuf:"varint,4,opt,name=error,proto3" json:"error,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetLoggerDetailsResponse) Reset() { - *x = GetLoggerDetailsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[99] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetLoggerDetailsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetLoggerDetailsResponse) ProtoMessage() {} - -func (x *GetLoggerDetailsResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[99] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetLoggerDetailsResponse.ProtoReflect.Descriptor instead. +func (m *GetLoggerDetailsResponse) Reset() { *m = GetLoggerDetailsResponse{} } +func (m *GetLoggerDetailsResponse) String() string { return proto.CompactTextString(m) } +func (*GetLoggerDetailsResponse) ProtoMessage() {} func (*GetLoggerDetailsResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{99} + return fileDescriptor_77a6da22d6a3feb1, []int{93} } -func (x *GetLoggerDetailsResponse) GetInfo() bool { - if x != nil { - return x.Info +func (m *GetLoggerDetailsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetLoggerDetailsResponse.Unmarshal(m, b) +} +func (m *GetLoggerDetailsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetLoggerDetailsResponse.Marshal(b, m, deterministic) +} +func (m *GetLoggerDetailsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetLoggerDetailsResponse.Merge(m, src) +} +func (m *GetLoggerDetailsResponse) XXX_Size() int { + return xxx_messageInfo_GetLoggerDetailsResponse.Size(m) +} +func (m *GetLoggerDetailsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetLoggerDetailsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetLoggerDetailsResponse proto.InternalMessageInfo + +func (m *GetLoggerDetailsResponse) GetInfo() bool { + if m != nil { + return m.Info } return false } -func (x *GetLoggerDetailsResponse) GetDebug() bool { - if x != nil { - return x.Debug +func (m *GetLoggerDetailsResponse) GetDebug() bool { + if m != nil { + return m.Debug } return false } -func (x *GetLoggerDetailsResponse) GetWarn() bool { - if x != nil { - return x.Warn +func (m *GetLoggerDetailsResponse) GetWarn() bool { + if m != nil { + return m.Warn } return false } -func (x *GetLoggerDetailsResponse) GetError() bool { - if x != nil { - return x.Error +func (m *GetLoggerDetailsResponse) GetError() bool { + if m != nil { + return m.Error } return false } type SetLoggerDetailsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Logger string `protobuf:"bytes,1,opt,name=logger,proto3" json:"logger,omitempty"` - Level string `protobuf:"bytes,2,opt,name=level,proto3" json:"level,omitempty"` + Logger string `protobuf:"bytes,1,opt,name=logger,proto3" json:"logger,omitempty"` + Level string `protobuf:"bytes,2,opt,name=level,proto3" json:"level,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *SetLoggerDetailsRequest) Reset() { - *x = SetLoggerDetailsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[100] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SetLoggerDetailsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetLoggerDetailsRequest) ProtoMessage() {} - -func (x *SetLoggerDetailsRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[100] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetLoggerDetailsRequest.ProtoReflect.Descriptor instead. +func (m *SetLoggerDetailsRequest) Reset() { *m = SetLoggerDetailsRequest{} } +func (m *SetLoggerDetailsRequest) String() string { return proto.CompactTextString(m) } +func (*SetLoggerDetailsRequest) ProtoMessage() {} func (*SetLoggerDetailsRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{100} + return fileDescriptor_77a6da22d6a3feb1, []int{94} } -func (x *SetLoggerDetailsRequest) GetLogger() string { - if x != nil { - return x.Logger +func (m *SetLoggerDetailsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SetLoggerDetailsRequest.Unmarshal(m, b) +} +func (m *SetLoggerDetailsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SetLoggerDetailsRequest.Marshal(b, m, deterministic) +} +func (m *SetLoggerDetailsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetLoggerDetailsRequest.Merge(m, src) +} +func (m *SetLoggerDetailsRequest) XXX_Size() int { + return xxx_messageInfo_SetLoggerDetailsRequest.Size(m) +} +func (m *SetLoggerDetailsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SetLoggerDetailsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SetLoggerDetailsRequest proto.InternalMessageInfo + +func (m *SetLoggerDetailsRequest) GetLogger() string { + if m != nil { + return m.Logger } return "" } -func (x *SetLoggerDetailsRequest) GetLevel() string { - if x != nil { - return x.Level +func (m *SetLoggerDetailsRequest) GetLevel() string { + if m != nil { + return m.Level } return "" } type GetExchangePairsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangePairsRequest) Reset() { - *x = GetExchangePairsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[101] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangePairsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangePairsRequest) ProtoMessage() {} - -func (x *GetExchangePairsRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[101] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangePairsRequest.ProtoReflect.Descriptor instead. +func (m *GetExchangePairsRequest) Reset() { *m = GetExchangePairsRequest{} } +func (m *GetExchangePairsRequest) String() string { return proto.CompactTextString(m) } +func (*GetExchangePairsRequest) ProtoMessage() {} func (*GetExchangePairsRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{101} + return fileDescriptor_77a6da22d6a3feb1, []int{95} } -func (x *GetExchangePairsRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetExchangePairsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangePairsRequest.Unmarshal(m, b) +} +func (m *GetExchangePairsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangePairsRequest.Marshal(b, m, deterministic) +} +func (m *GetExchangePairsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangePairsRequest.Merge(m, src) +} +func (m *GetExchangePairsRequest) XXX_Size() int { + return xxx_messageInfo_GetExchangePairsRequest.Size(m) +} +func (m *GetExchangePairsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangePairsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangePairsRequest proto.InternalMessageInfo + +func (m *GetExchangePairsRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetExchangePairsRequest) GetAsset() string { - if x != nil { - return x.Asset +func (m *GetExchangePairsRequest) GetAsset() string { + if m != nil { + return m.Asset } return "" } type GetExchangePairsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - SupportedAssets map[string]*PairsSupported `protobuf:"bytes,1,rep,name=supported_assets,json=supportedAssets,proto3" json:"supported_assets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + SupportedAssets map[string]*PairsSupported `protobuf:"bytes,1,rep,name=supported_assets,json=supportedAssets,proto3" json:"supported_assets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangePairsResponse) Reset() { - *x = GetExchangePairsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[102] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangePairsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangePairsResponse) ProtoMessage() {} - -func (x *GetExchangePairsResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[102] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangePairsResponse.ProtoReflect.Descriptor instead. +func (m *GetExchangePairsResponse) Reset() { *m = GetExchangePairsResponse{} } +func (m *GetExchangePairsResponse) String() string { return proto.CompactTextString(m) } +func (*GetExchangePairsResponse) ProtoMessage() {} func (*GetExchangePairsResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{102} + return fileDescriptor_77a6da22d6a3feb1, []int{96} } -func (x *GetExchangePairsResponse) GetSupportedAssets() map[string]*PairsSupported { - if x != nil { - return x.SupportedAssets +func (m *GetExchangePairsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangePairsResponse.Unmarshal(m, b) +} +func (m *GetExchangePairsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangePairsResponse.Marshal(b, m, deterministic) +} +func (m *GetExchangePairsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangePairsResponse.Merge(m, src) +} +func (m *GetExchangePairsResponse) XXX_Size() int { + return xxx_messageInfo_GetExchangePairsResponse.Size(m) +} +func (m *GetExchangePairsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangePairsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangePairsResponse proto.InternalMessageInfo + +func (m *GetExchangePairsResponse) GetSupportedAssets() map[string]*PairsSupported { + if m != nil { + return m.SupportedAssets } return nil } -type ExchangePairRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - AssetType string `protobuf:"bytes,2,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,3,opt,name=pair,proto3" json:"pair,omitempty"` +type SetExchangePairRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + AssetType string `protobuf:"bytes,2,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Pairs []*CurrencyPair `protobuf:"bytes,3,rep,name=pairs,proto3" json:"pairs,omitempty"` + Enable bool `protobuf:"varint,4,opt,name=enable,proto3" json:"enable,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *ExchangePairRequest) Reset() { - *x = ExchangePairRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[103] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *SetExchangePairRequest) Reset() { *m = SetExchangePairRequest{} } +func (m *SetExchangePairRequest) String() string { return proto.CompactTextString(m) } +func (*SetExchangePairRequest) ProtoMessage() {} +func (*SetExchangePairRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{97} } -func (x *ExchangePairRequest) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *SetExchangePairRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SetExchangePairRequest.Unmarshal(m, b) +} +func (m *SetExchangePairRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SetExchangePairRequest.Marshal(b, m, deterministic) +} +func (m *SetExchangePairRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetExchangePairRequest.Merge(m, src) +} +func (m *SetExchangePairRequest) XXX_Size() int { + return xxx_messageInfo_SetExchangePairRequest.Size(m) +} +func (m *SetExchangePairRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SetExchangePairRequest.DiscardUnknown(m) } -func (*ExchangePairRequest) ProtoMessage() {} +var xxx_messageInfo_SetExchangePairRequest proto.InternalMessageInfo -func (x *ExchangePairRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[103] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExchangePairRequest.ProtoReflect.Descriptor instead. -func (*ExchangePairRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{103} -} - -func (x *ExchangePairRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *SetExchangePairRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *ExchangePairRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *SetExchangePairRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } -func (x *ExchangePairRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *SetExchangePairRequest) GetPairs() []*CurrencyPair { + if m != nil { + return m.Pairs } return nil } +func (m *SetExchangePairRequest) GetEnable() bool { + if m != nil { + return m.Enable + } + return false +} + type GetOrderbookStreamRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetOrderbookStreamRequest) Reset() { - *x = GetOrderbookStreamRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[104] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetOrderbookStreamRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetOrderbookStreamRequest) ProtoMessage() {} - -func (x *GetOrderbookStreamRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[104] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetOrderbookStreamRequest.ProtoReflect.Descriptor instead. +func (m *GetOrderbookStreamRequest) Reset() { *m = GetOrderbookStreamRequest{} } +func (m *GetOrderbookStreamRequest) String() string { return proto.CompactTextString(m) } +func (*GetOrderbookStreamRequest) ProtoMessage() {} func (*GetOrderbookStreamRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{104} + return fileDescriptor_77a6da22d6a3feb1, []int{98} } -func (x *GetOrderbookStreamRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetOrderbookStreamRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetOrderbookStreamRequest.Unmarshal(m, b) +} +func (m *GetOrderbookStreamRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetOrderbookStreamRequest.Marshal(b, m, deterministic) +} +func (m *GetOrderbookStreamRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetOrderbookStreamRequest.Merge(m, src) +} +func (m *GetOrderbookStreamRequest) XXX_Size() int { + return xxx_messageInfo_GetOrderbookStreamRequest.Size(m) +} +func (m *GetOrderbookStreamRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetOrderbookStreamRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetOrderbookStreamRequest proto.InternalMessageInfo + +func (m *GetOrderbookStreamRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetOrderbookStreamRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *GetOrderbookStreamRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *GetOrderbookStreamRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *GetOrderbookStreamRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } type GetExchangeOrderbookStreamRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangeOrderbookStreamRequest) Reset() { - *x = GetExchangeOrderbookStreamRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[105] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangeOrderbookStreamRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangeOrderbookStreamRequest) ProtoMessage() {} - -func (x *GetExchangeOrderbookStreamRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[105] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangeOrderbookStreamRequest.ProtoReflect.Descriptor instead. +func (m *GetExchangeOrderbookStreamRequest) Reset() { *m = GetExchangeOrderbookStreamRequest{} } +func (m *GetExchangeOrderbookStreamRequest) String() string { return proto.CompactTextString(m) } +func (*GetExchangeOrderbookStreamRequest) ProtoMessage() {} func (*GetExchangeOrderbookStreamRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{105} + return fileDescriptor_77a6da22d6a3feb1, []int{99} } -func (x *GetExchangeOrderbookStreamRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetExchangeOrderbookStreamRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeOrderbookStreamRequest.Unmarshal(m, b) +} +func (m *GetExchangeOrderbookStreamRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeOrderbookStreamRequest.Marshal(b, m, deterministic) +} +func (m *GetExchangeOrderbookStreamRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeOrderbookStreamRequest.Merge(m, src) +} +func (m *GetExchangeOrderbookStreamRequest) XXX_Size() int { + return xxx_messageInfo_GetExchangeOrderbookStreamRequest.Size(m) +} +func (m *GetExchangeOrderbookStreamRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeOrderbookStreamRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeOrderbookStreamRequest proto.InternalMessageInfo + +func (m *GetExchangeOrderbookStreamRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } type GetTickerStreamRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetTickerStreamRequest) Reset() { - *x = GetTickerStreamRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[106] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetTickerStreamRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetTickerStreamRequest) ProtoMessage() {} - -func (x *GetTickerStreamRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[106] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetTickerStreamRequest.ProtoReflect.Descriptor instead. +func (m *GetTickerStreamRequest) Reset() { *m = GetTickerStreamRequest{} } +func (m *GetTickerStreamRequest) String() string { return proto.CompactTextString(m) } +func (*GetTickerStreamRequest) ProtoMessage() {} func (*GetTickerStreamRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{106} + return fileDescriptor_77a6da22d6a3feb1, []int{100} } -func (x *GetTickerStreamRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetTickerStreamRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetTickerStreamRequest.Unmarshal(m, b) +} +func (m *GetTickerStreamRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetTickerStreamRequest.Marshal(b, m, deterministic) +} +func (m *GetTickerStreamRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetTickerStreamRequest.Merge(m, src) +} +func (m *GetTickerStreamRequest) XXX_Size() int { + return xxx_messageInfo_GetTickerStreamRequest.Size(m) +} +func (m *GetTickerStreamRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetTickerStreamRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetTickerStreamRequest proto.InternalMessageInfo + +func (m *GetTickerStreamRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetTickerStreamRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *GetTickerStreamRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *GetTickerStreamRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *GetTickerStreamRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } type GetExchangeTickerStreamRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetExchangeTickerStreamRequest) Reset() { - *x = GetExchangeTickerStreamRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[107] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetExchangeTickerStreamRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetExchangeTickerStreamRequest) ProtoMessage() {} - -func (x *GetExchangeTickerStreamRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[107] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetExchangeTickerStreamRequest.ProtoReflect.Descriptor instead. +func (m *GetExchangeTickerStreamRequest) Reset() { *m = GetExchangeTickerStreamRequest{} } +func (m *GetExchangeTickerStreamRequest) String() string { return proto.CompactTextString(m) } +func (*GetExchangeTickerStreamRequest) ProtoMessage() {} func (*GetExchangeTickerStreamRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{107} + return fileDescriptor_77a6da22d6a3feb1, []int{101} } -func (x *GetExchangeTickerStreamRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetExchangeTickerStreamRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeTickerStreamRequest.Unmarshal(m, b) +} +func (m *GetExchangeTickerStreamRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeTickerStreamRequest.Marshal(b, m, deterministic) +} +func (m *GetExchangeTickerStreamRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeTickerStreamRequest.Merge(m, src) +} +func (m *GetExchangeTickerStreamRequest) XXX_Size() int { + return xxx_messageInfo_GetExchangeTickerStreamRequest.Size(m) +} +func (m *GetExchangeTickerStreamRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeTickerStreamRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeTickerStreamRequest proto.InternalMessageInfo + +func (m *GetExchangeTickerStreamRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } type GetAuditEventRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - StartDate string `protobuf:"bytes,1,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` - EndDate string `protobuf:"bytes,2,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` - OrderBy string `protobuf:"bytes,3,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"` - Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` - Offset int32 `protobuf:"varint,5,opt,name=offset,proto3" json:"offset,omitempty"` + StartDate string `protobuf:"bytes,1,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` + EndDate string `protobuf:"bytes,2,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` + OrderBy string `protobuf:"bytes,3,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"` + Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,5,opt,name=offset,proto3" json:"offset,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetAuditEventRequest) Reset() { - *x = GetAuditEventRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[108] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetAuditEventRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetAuditEventRequest) ProtoMessage() {} - -func (x *GetAuditEventRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[108] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetAuditEventRequest.ProtoReflect.Descriptor instead. +func (m *GetAuditEventRequest) Reset() { *m = GetAuditEventRequest{} } +func (m *GetAuditEventRequest) String() string { return proto.CompactTextString(m) } +func (*GetAuditEventRequest) ProtoMessage() {} func (*GetAuditEventRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{108} + return fileDescriptor_77a6da22d6a3feb1, []int{102} } -func (x *GetAuditEventRequest) GetStartDate() string { - if x != nil { - return x.StartDate +func (m *GetAuditEventRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetAuditEventRequest.Unmarshal(m, b) +} +func (m *GetAuditEventRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetAuditEventRequest.Marshal(b, m, deterministic) +} +func (m *GetAuditEventRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetAuditEventRequest.Merge(m, src) +} +func (m *GetAuditEventRequest) XXX_Size() int { + return xxx_messageInfo_GetAuditEventRequest.Size(m) +} +func (m *GetAuditEventRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetAuditEventRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetAuditEventRequest proto.InternalMessageInfo + +func (m *GetAuditEventRequest) GetStartDate() string { + if m != nil { + return m.StartDate } return "" } -func (x *GetAuditEventRequest) GetEndDate() string { - if x != nil { - return x.EndDate +func (m *GetAuditEventRequest) GetEndDate() string { + if m != nil { + return m.EndDate } return "" } -func (x *GetAuditEventRequest) GetOrderBy() string { - if x != nil { - return x.OrderBy +func (m *GetAuditEventRequest) GetOrderBy() string { + if m != nil { + return m.OrderBy } return "" } -func (x *GetAuditEventRequest) GetLimit() int32 { - if x != nil { - return x.Limit +func (m *GetAuditEventRequest) GetLimit() int32 { + if m != nil { + return m.Limit } return 0 } -func (x *GetAuditEventRequest) GetOffset() int32 { - if x != nil { - return x.Offset +func (m *GetAuditEventRequest) GetOffset() int32 { + if m != nil { + return m.Offset } return 0 } type GetAuditEventResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Events []*AuditEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + Events []*AuditEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetAuditEventResponse) Reset() { - *x = GetAuditEventResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[109] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetAuditEventResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetAuditEventResponse) ProtoMessage() {} - -func (x *GetAuditEventResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[109] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetAuditEventResponse.ProtoReflect.Descriptor instead. +func (m *GetAuditEventResponse) Reset() { *m = GetAuditEventResponse{} } +func (m *GetAuditEventResponse) String() string { return proto.CompactTextString(m) } +func (*GetAuditEventResponse) ProtoMessage() {} func (*GetAuditEventResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{109} + return fileDescriptor_77a6da22d6a3feb1, []int{103} } -func (x *GetAuditEventResponse) GetEvents() []*AuditEvent { - if x != nil { - return x.Events +func (m *GetAuditEventResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetAuditEventResponse.Unmarshal(m, b) +} +func (m *GetAuditEventResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetAuditEventResponse.Marshal(b, m, deterministic) +} +func (m *GetAuditEventResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetAuditEventResponse.Merge(m, src) +} +func (m *GetAuditEventResponse) XXX_Size() int { + return xxx_messageInfo_GetAuditEventResponse.Size(m) +} +func (m *GetAuditEventResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetAuditEventResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetAuditEventResponse proto.InternalMessageInfo + +func (m *GetAuditEventResponse) GetEvents() []*AuditEvent { + if m != nil { + return m.Events } return nil } type GetHistoricCandlesRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` - Start int64 `protobuf:"varint,4,opt,name=start,proto3" json:"start,omitempty"` - End int64 `protobuf:"varint,5,opt,name=end,proto3" json:"end,omitempty"` - TimeInterval int64 `protobuf:"varint,6,opt,name=time_interval,json=timeInterval,proto3" json:"time_interval,omitempty"` - ExRequest bool `protobuf:"varint,7,opt,name=ex_request,json=exRequest,proto3" json:"ex_request,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + AssetType string `protobuf:"bytes,3,opt,name=asset_type,json=assetType,proto3" json:"asset_type,omitempty"` + Start int64 `protobuf:"varint,4,opt,name=start,proto3" json:"start,omitempty"` + End int64 `protobuf:"varint,5,opt,name=end,proto3" json:"end,omitempty"` + TimeInterval int64 `protobuf:"varint,6,opt,name=time_interval,json=timeInterval,proto3" json:"time_interval,omitempty"` + ExRequest bool `protobuf:"varint,7,opt,name=ex_request,json=exRequest,proto3" json:"ex_request,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetHistoricCandlesRequest) Reset() { - *x = GetHistoricCandlesRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[110] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetHistoricCandlesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetHistoricCandlesRequest) ProtoMessage() {} - -func (x *GetHistoricCandlesRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[110] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetHistoricCandlesRequest.ProtoReflect.Descriptor instead. +func (m *GetHistoricCandlesRequest) Reset() { *m = GetHistoricCandlesRequest{} } +func (m *GetHistoricCandlesRequest) String() string { return proto.CompactTextString(m) } +func (*GetHistoricCandlesRequest) ProtoMessage() {} func (*GetHistoricCandlesRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{110} + return fileDescriptor_77a6da22d6a3feb1, []int{104} } -func (x *GetHistoricCandlesRequest) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetHistoricCandlesRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetHistoricCandlesRequest.Unmarshal(m, b) +} +func (m *GetHistoricCandlesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetHistoricCandlesRequest.Marshal(b, m, deterministic) +} +func (m *GetHistoricCandlesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetHistoricCandlesRequest.Merge(m, src) +} +func (m *GetHistoricCandlesRequest) XXX_Size() int { + return xxx_messageInfo_GetHistoricCandlesRequest.Size(m) +} +func (m *GetHistoricCandlesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetHistoricCandlesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetHistoricCandlesRequest proto.InternalMessageInfo + +func (m *GetHistoricCandlesRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetHistoricCandlesRequest) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *GetHistoricCandlesRequest) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *GetHistoricCandlesRequest) GetAssetType() string { - if x != nil { - return x.AssetType +func (m *GetHistoricCandlesRequest) GetAssetType() string { + if m != nil { + return m.AssetType } return "" } -func (x *GetHistoricCandlesRequest) GetStart() int64 { - if x != nil { - return x.Start +func (m *GetHistoricCandlesRequest) GetStart() int64 { + if m != nil { + return m.Start } return 0 } -func (x *GetHistoricCandlesRequest) GetEnd() int64 { - if x != nil { - return x.End +func (m *GetHistoricCandlesRequest) GetEnd() int64 { + if m != nil { + return m.End } return 0 } -func (x *GetHistoricCandlesRequest) GetTimeInterval() int64 { - if x != nil { - return x.TimeInterval +func (m *GetHistoricCandlesRequest) GetTimeInterval() int64 { + if m != nil { + return m.TimeInterval } return 0 } -func (x *GetHistoricCandlesRequest) GetExRequest() bool { - if x != nil { - return x.ExRequest +func (m *GetHistoricCandlesRequest) GetExRequest() bool { + if m != nil { + return m.ExRequest } return false } type GetHistoricCandlesResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` - Start int64 `protobuf:"varint,3,opt,name=start,proto3" json:"start,omitempty"` - End int64 `protobuf:"varint,4,opt,name=end,proto3" json:"end,omitempty"` - Interval string `protobuf:"bytes,6,opt,name=interval,proto3" json:"interval,omitempty"` - Candle []*Candle `protobuf:"bytes,5,rep,name=candle,proto3" json:"candle,omitempty"` + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Pair *CurrencyPair `protobuf:"bytes,2,opt,name=pair,proto3" json:"pair,omitempty"` + Start int64 `protobuf:"varint,3,opt,name=start,proto3" json:"start,omitempty"` + End int64 `protobuf:"varint,4,opt,name=end,proto3" json:"end,omitempty"` + Interval string `protobuf:"bytes,6,opt,name=interval,proto3" json:"interval,omitempty"` + Candle []*Candle `protobuf:"bytes,5,rep,name=candle,proto3" json:"candle,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GetHistoricCandlesResponse) Reset() { - *x = GetHistoricCandlesResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[111] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetHistoricCandlesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetHistoricCandlesResponse) ProtoMessage() {} - -func (x *GetHistoricCandlesResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[111] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetHistoricCandlesResponse.ProtoReflect.Descriptor instead. +func (m *GetHistoricCandlesResponse) Reset() { *m = GetHistoricCandlesResponse{} } +func (m *GetHistoricCandlesResponse) String() string { return proto.CompactTextString(m) } +func (*GetHistoricCandlesResponse) ProtoMessage() {} func (*GetHistoricCandlesResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{111} + return fileDescriptor_77a6da22d6a3feb1, []int{105} } -func (x *GetHistoricCandlesResponse) GetExchange() string { - if x != nil { - return x.Exchange +func (m *GetHistoricCandlesResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetHistoricCandlesResponse.Unmarshal(m, b) +} +func (m *GetHistoricCandlesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetHistoricCandlesResponse.Marshal(b, m, deterministic) +} +func (m *GetHistoricCandlesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetHistoricCandlesResponse.Merge(m, src) +} +func (m *GetHistoricCandlesResponse) XXX_Size() int { + return xxx_messageInfo_GetHistoricCandlesResponse.Size(m) +} +func (m *GetHistoricCandlesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetHistoricCandlesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetHistoricCandlesResponse proto.InternalMessageInfo + +func (m *GetHistoricCandlesResponse) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *GetHistoricCandlesResponse) GetPair() *CurrencyPair { - if x != nil { - return x.Pair +func (m *GetHistoricCandlesResponse) GetPair() *CurrencyPair { + if m != nil { + return m.Pair } return nil } -func (x *GetHistoricCandlesResponse) GetStart() int64 { - if x != nil { - return x.Start +func (m *GetHistoricCandlesResponse) GetStart() int64 { + if m != nil { + return m.Start } return 0 } -func (x *GetHistoricCandlesResponse) GetEnd() int64 { - if x != nil { - return x.End +func (m *GetHistoricCandlesResponse) GetEnd() int64 { + if m != nil { + return m.End } return 0 } -func (x *GetHistoricCandlesResponse) GetInterval() string { - if x != nil { - return x.Interval +func (m *GetHistoricCandlesResponse) GetInterval() string { + if m != nil { + return m.Interval } return "" } -func (x *GetHistoricCandlesResponse) GetCandle() []*Candle { - if x != nil { - return x.Candle +func (m *GetHistoricCandlesResponse) GetCandle() []*Candle { + if m != nil { + return m.Candle } return nil } type Candle struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Time int64 `protobuf:"varint,1,opt,name=time,proto3" json:"time,omitempty"` - Low float64 `protobuf:"fixed64,2,opt,name=low,proto3" json:"low,omitempty"` - High float64 `protobuf:"fixed64,3,opt,name=high,proto3" json:"high,omitempty"` - Open float64 `protobuf:"fixed64,4,opt,name=open,proto3" json:"open,omitempty"` - Close float64 `protobuf:"fixed64,5,opt,name=close,proto3" json:"close,omitempty"` - Volume float64 `protobuf:"fixed64,6,opt,name=volume,proto3" json:"volume,omitempty"` + Time int64 `protobuf:"varint,1,opt,name=time,proto3" json:"time,omitempty"` + Low float64 `protobuf:"fixed64,2,opt,name=low,proto3" json:"low,omitempty"` + High float64 `protobuf:"fixed64,3,opt,name=high,proto3" json:"high,omitempty"` + Open float64 `protobuf:"fixed64,4,opt,name=open,proto3" json:"open,omitempty"` + Close float64 `protobuf:"fixed64,5,opt,name=close,proto3" json:"close,omitempty"` + Volume float64 `protobuf:"fixed64,6,opt,name=volume,proto3" json:"volume,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *Candle) Reset() { - *x = Candle{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[112] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Candle) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Candle) ProtoMessage() {} - -func (x *Candle) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[112] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Candle.ProtoReflect.Descriptor instead. +func (m *Candle) Reset() { *m = Candle{} } +func (m *Candle) String() string { return proto.CompactTextString(m) } +func (*Candle) ProtoMessage() {} func (*Candle) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{112} + return fileDescriptor_77a6da22d6a3feb1, []int{106} } -func (x *Candle) GetTime() int64 { - if x != nil { - return x.Time +func (m *Candle) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Candle.Unmarshal(m, b) +} +func (m *Candle) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Candle.Marshal(b, m, deterministic) +} +func (m *Candle) XXX_Merge(src proto.Message) { + xxx_messageInfo_Candle.Merge(m, src) +} +func (m *Candle) XXX_Size() int { + return xxx_messageInfo_Candle.Size(m) +} +func (m *Candle) XXX_DiscardUnknown() { + xxx_messageInfo_Candle.DiscardUnknown(m) +} + +var xxx_messageInfo_Candle proto.InternalMessageInfo + +func (m *Candle) GetTime() int64 { + if m != nil { + return m.Time } return 0 } -func (x *Candle) GetLow() float64 { - if x != nil { - return x.Low +func (m *Candle) GetLow() float64 { + if m != nil { + return m.Low } return 0 } -func (x *Candle) GetHigh() float64 { - if x != nil { - return x.High +func (m *Candle) GetHigh() float64 { + if m != nil { + return m.High } return 0 } -func (x *Candle) GetOpen() float64 { - if x != nil { - return x.Open +func (m *Candle) GetOpen() float64 { + if m != nil { + return m.Open } return 0 } -func (x *Candle) GetClose() float64 { - if x != nil { - return x.Close +func (m *Candle) GetClose() float64 { + if m != nil { + return m.Close } return 0 } -func (x *Candle) GetVolume() float64 { - if x != nil { - return x.Volume +func (m *Candle) GetVolume() float64 { + if m != nil { + return m.Volume } return 0 } type AuditEvent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - Identifier string `protobuf:"bytes,2,opt,name=identifier,proto3" json:"identifier,omitempty"` - Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` - Timestamp string `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Identifier string `protobuf:"bytes,2,opt,name=identifier,proto3" json:"identifier,omitempty"` + Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` + Timestamp string `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *AuditEvent) Reset() { - *x = AuditEvent{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[113] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AuditEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AuditEvent) ProtoMessage() {} - -func (x *AuditEvent) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[113] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AuditEvent.ProtoReflect.Descriptor instead. +func (m *AuditEvent) Reset() { *m = AuditEvent{} } +func (m *AuditEvent) String() string { return proto.CompactTextString(m) } +func (*AuditEvent) ProtoMessage() {} func (*AuditEvent) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{113} + return fileDescriptor_77a6da22d6a3feb1, []int{107} } -func (x *AuditEvent) GetType() string { - if x != nil { - return x.Type +func (m *AuditEvent) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AuditEvent.Unmarshal(m, b) +} +func (m *AuditEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AuditEvent.Marshal(b, m, deterministic) +} +func (m *AuditEvent) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuditEvent.Merge(m, src) +} +func (m *AuditEvent) XXX_Size() int { + return xxx_messageInfo_AuditEvent.Size(m) +} +func (m *AuditEvent) XXX_DiscardUnknown() { + xxx_messageInfo_AuditEvent.DiscardUnknown(m) +} + +var xxx_messageInfo_AuditEvent proto.InternalMessageInfo + +func (m *AuditEvent) GetType() string { + if m != nil { + return m.Type } return "" } -func (x *AuditEvent) GetIdentifier() string { - if x != nil { - return x.Identifier +func (m *AuditEvent) GetIdentifier() string { + if m != nil { + return m.Identifier } return "" } -func (x *AuditEvent) GetMessage() string { - if x != nil { - return x.Message +func (m *AuditEvent) GetMessage() string { + if m != nil { + return m.Message } return "" } -func (x *AuditEvent) GetTimestamp() string { - if x != nil { - return x.Timestamp +func (m *AuditEvent) GetTimestamp() string { + if m != nil { + return m.Timestamp } return "" } type GCTScript struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UUID string `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` - NextRun string `protobuf:"bytes,4,opt,name=next_run,json=nextRun,proto3" json:"next_run,omitempty"` + UUID string `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` + NextRun string `protobuf:"bytes,4,opt,name=next_run,json=nextRun,proto3" json:"next_run,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScript) Reset() { - *x = GCTScript{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[114] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScript) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScript) ProtoMessage() {} - -func (x *GCTScript) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[114] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScript.ProtoReflect.Descriptor instead. +func (m *GCTScript) Reset() { *m = GCTScript{} } +func (m *GCTScript) String() string { return proto.CompactTextString(m) } +func (*GCTScript) ProtoMessage() {} func (*GCTScript) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{114} + return fileDescriptor_77a6da22d6a3feb1, []int{108} } -func (x *GCTScript) GetUUID() string { - if x != nil { - return x.UUID +func (m *GCTScript) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScript.Unmarshal(m, b) +} +func (m *GCTScript) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScript.Marshal(b, m, deterministic) +} +func (m *GCTScript) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScript.Merge(m, src) +} +func (m *GCTScript) XXX_Size() int { + return xxx_messageInfo_GCTScript.Size(m) +} +func (m *GCTScript) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScript.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScript proto.InternalMessageInfo + +func (m *GCTScript) GetUUID() string { + if m != nil { + return m.UUID } return "" } -func (x *GCTScript) GetName() string { - if x != nil { - return x.Name +func (m *GCTScript) GetName() string { + if m != nil { + return m.Name } return "" } -func (x *GCTScript) GetPath() string { - if x != nil { - return x.Path +func (m *GCTScript) GetPath() string { + if m != nil { + return m.Path } return "" } -func (x *GCTScript) GetNextRun() string { - if x != nil { - return x.NextRun +func (m *GCTScript) GetNextRun() string { + if m != nil { + return m.NextRun } return "" } type GCTScriptExecuteRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Script *GCTScript `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + Script *GCTScript `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptExecuteRequest) Reset() { - *x = GCTScriptExecuteRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[115] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptExecuteRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptExecuteRequest) ProtoMessage() {} - -func (x *GCTScriptExecuteRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[115] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptExecuteRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptExecuteRequest) Reset() { *m = GCTScriptExecuteRequest{} } +func (m *GCTScriptExecuteRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptExecuteRequest) ProtoMessage() {} func (*GCTScriptExecuteRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{115} + return fileDescriptor_77a6da22d6a3feb1, []int{109} } -func (x *GCTScriptExecuteRequest) GetScript() *GCTScript { - if x != nil { - return x.Script +func (m *GCTScriptExecuteRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptExecuteRequest.Unmarshal(m, b) +} +func (m *GCTScriptExecuteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptExecuteRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptExecuteRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptExecuteRequest.Merge(m, src) +} +func (m *GCTScriptExecuteRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptExecuteRequest.Size(m) +} +func (m *GCTScriptExecuteRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptExecuteRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptExecuteRequest proto.InternalMessageInfo + +func (m *GCTScriptExecuteRequest) GetScript() *GCTScript { + if m != nil { + return m.Script } return nil } type GCTScriptStopRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Script *GCTScript `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + Script *GCTScript `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptStopRequest) Reset() { - *x = GCTScriptStopRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[116] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptStopRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptStopRequest) ProtoMessage() {} - -func (x *GCTScriptStopRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[116] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptStopRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptStopRequest) Reset() { *m = GCTScriptStopRequest{} } +func (m *GCTScriptStopRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptStopRequest) ProtoMessage() {} func (*GCTScriptStopRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{116} + return fileDescriptor_77a6da22d6a3feb1, []int{110} } -func (x *GCTScriptStopRequest) GetScript() *GCTScript { - if x != nil { - return x.Script +func (m *GCTScriptStopRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptStopRequest.Unmarshal(m, b) +} +func (m *GCTScriptStopRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptStopRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptStopRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptStopRequest.Merge(m, src) +} +func (m *GCTScriptStopRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptStopRequest.Size(m) +} +func (m *GCTScriptStopRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptStopRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptStopRequest proto.InternalMessageInfo + +func (m *GCTScriptStopRequest) GetScript() *GCTScript { + if m != nil { + return m.Script } return nil } type GCTScriptStopAllRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptStopAllRequest) Reset() { - *x = GCTScriptStopAllRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[117] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptStopAllRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptStopAllRequest) ProtoMessage() {} - -func (x *GCTScriptStopAllRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[117] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptStopAllRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptStopAllRequest) Reset() { *m = GCTScriptStopAllRequest{} } +func (m *GCTScriptStopAllRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptStopAllRequest) ProtoMessage() {} func (*GCTScriptStopAllRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{117} + return fileDescriptor_77a6da22d6a3feb1, []int{111} } +func (m *GCTScriptStopAllRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptStopAllRequest.Unmarshal(m, b) +} +func (m *GCTScriptStopAllRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptStopAllRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptStopAllRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptStopAllRequest.Merge(m, src) +} +func (m *GCTScriptStopAllRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptStopAllRequest.Size(m) +} +func (m *GCTScriptStopAllRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptStopAllRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptStopAllRequest proto.InternalMessageInfo + type GCTScriptStatusRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptStatusRequest) Reset() { - *x = GCTScriptStatusRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[118] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptStatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptStatusRequest) ProtoMessage() {} - -func (x *GCTScriptStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[118] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptStatusRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptStatusRequest) Reset() { *m = GCTScriptStatusRequest{} } +func (m *GCTScriptStatusRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptStatusRequest) ProtoMessage() {} func (*GCTScriptStatusRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{118} + return fileDescriptor_77a6da22d6a3feb1, []int{112} } +func (m *GCTScriptStatusRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptStatusRequest.Unmarshal(m, b) +} +func (m *GCTScriptStatusRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptStatusRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptStatusRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptStatusRequest.Merge(m, src) +} +func (m *GCTScriptStatusRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptStatusRequest.Size(m) +} +func (m *GCTScriptStatusRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptStatusRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptStatusRequest proto.InternalMessageInfo + type GCTScriptListAllRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptListAllRequest) Reset() { - *x = GCTScriptListAllRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[119] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptListAllRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptListAllRequest) ProtoMessage() {} - -func (x *GCTScriptListAllRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[119] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptListAllRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptListAllRequest) Reset() { *m = GCTScriptListAllRequest{} } +func (m *GCTScriptListAllRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptListAllRequest) ProtoMessage() {} func (*GCTScriptListAllRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{119} + return fileDescriptor_77a6da22d6a3feb1, []int{113} } +func (m *GCTScriptListAllRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptListAllRequest.Unmarshal(m, b) +} +func (m *GCTScriptListAllRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptListAllRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptListAllRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptListAllRequest.Merge(m, src) +} +func (m *GCTScriptListAllRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptListAllRequest.Size(m) +} +func (m *GCTScriptListAllRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptListAllRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptListAllRequest proto.InternalMessageInfo + type GCTScriptUploadRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ScriptName string `protobuf:"bytes,1,opt,name=script_name,json=scriptName,proto3" json:"script_name,omitempty"` - ScriptData string `protobuf:"bytes,2,opt,name=script_data,json=scriptData,proto3" json:"script_data,omitempty"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - Archived bool `protobuf:"varint,4,opt,name=archived,proto3" json:"archived,omitempty"` - Overwrite bool `protobuf:"varint,5,opt,name=overwrite,proto3" json:"overwrite,omitempty"` + ScriptName string `protobuf:"bytes,1,opt,name=script_name,json=scriptName,proto3" json:"script_name,omitempty"` + ScriptData string `protobuf:"bytes,2,opt,name=script_data,json=scriptData,proto3" json:"script_data,omitempty"` + Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Archived bool `protobuf:"varint,4,opt,name=archived,proto3" json:"archived,omitempty"` + Overwrite bool `protobuf:"varint,5,opt,name=overwrite,proto3" json:"overwrite,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptUploadRequest) Reset() { - *x = GCTScriptUploadRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[120] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptUploadRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptUploadRequest) ProtoMessage() {} - -func (x *GCTScriptUploadRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[120] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptUploadRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptUploadRequest) Reset() { *m = GCTScriptUploadRequest{} } +func (m *GCTScriptUploadRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptUploadRequest) ProtoMessage() {} func (*GCTScriptUploadRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{120} + return fileDescriptor_77a6da22d6a3feb1, []int{114} } -func (x *GCTScriptUploadRequest) GetScriptName() string { - if x != nil { - return x.ScriptName +func (m *GCTScriptUploadRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptUploadRequest.Unmarshal(m, b) +} +func (m *GCTScriptUploadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptUploadRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptUploadRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptUploadRequest.Merge(m, src) +} +func (m *GCTScriptUploadRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptUploadRequest.Size(m) +} +func (m *GCTScriptUploadRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptUploadRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptUploadRequest proto.InternalMessageInfo + +func (m *GCTScriptUploadRequest) GetScriptName() string { + if m != nil { + return m.ScriptName } return "" } -func (x *GCTScriptUploadRequest) GetScriptData() string { - if x != nil { - return x.ScriptData +func (m *GCTScriptUploadRequest) GetScriptData() string { + if m != nil { + return m.ScriptData } return "" } -func (x *GCTScriptUploadRequest) GetData() []byte { - if x != nil { - return x.Data +func (m *GCTScriptUploadRequest) GetData() []byte { + if m != nil { + return m.Data } return nil } -func (x *GCTScriptUploadRequest) GetArchived() bool { - if x != nil { - return x.Archived +func (m *GCTScriptUploadRequest) GetArchived() bool { + if m != nil { + return m.Archived } return false } -func (x *GCTScriptUploadRequest) GetOverwrite() bool { - if x != nil { - return x.Overwrite +func (m *GCTScriptUploadRequest) GetOverwrite() bool { + if m != nil { + return m.Overwrite } return false } type GCTScriptReadScriptRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Script *GCTScript `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + Script *GCTScript `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptReadScriptRequest) Reset() { - *x = GCTScriptReadScriptRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[121] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptReadScriptRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptReadScriptRequest) ProtoMessage() {} - -func (x *GCTScriptReadScriptRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[121] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptReadScriptRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptReadScriptRequest) Reset() { *m = GCTScriptReadScriptRequest{} } +func (m *GCTScriptReadScriptRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptReadScriptRequest) ProtoMessage() {} func (*GCTScriptReadScriptRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{121} + return fileDescriptor_77a6da22d6a3feb1, []int{115} } -func (x *GCTScriptReadScriptRequest) GetScript() *GCTScript { - if x != nil { - return x.Script +func (m *GCTScriptReadScriptRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptReadScriptRequest.Unmarshal(m, b) +} +func (m *GCTScriptReadScriptRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptReadScriptRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptReadScriptRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptReadScriptRequest.Merge(m, src) +} +func (m *GCTScriptReadScriptRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptReadScriptRequest.Size(m) +} +func (m *GCTScriptReadScriptRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptReadScriptRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptReadScriptRequest proto.InternalMessageInfo + +func (m *GCTScriptReadScriptRequest) GetScript() *GCTScript { + if m != nil { + return m.Script } return nil } type GCTScriptQueryRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Script *GCTScript `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + Script *GCTScript `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptQueryRequest) Reset() { - *x = GCTScriptQueryRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[122] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptQueryRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptQueryRequest) ProtoMessage() {} - -func (x *GCTScriptQueryRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[122] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptQueryRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptQueryRequest) Reset() { *m = GCTScriptQueryRequest{} } +func (m *GCTScriptQueryRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptQueryRequest) ProtoMessage() {} func (*GCTScriptQueryRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{122} + return fileDescriptor_77a6da22d6a3feb1, []int{116} } -func (x *GCTScriptQueryRequest) GetScript() *GCTScript { - if x != nil { - return x.Script +func (m *GCTScriptQueryRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptQueryRequest.Unmarshal(m, b) +} +func (m *GCTScriptQueryRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptQueryRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptQueryRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptQueryRequest.Merge(m, src) +} +func (m *GCTScriptQueryRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptQueryRequest.Size(m) +} +func (m *GCTScriptQueryRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptQueryRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptQueryRequest proto.InternalMessageInfo + +func (m *GCTScriptQueryRequest) GetScript() *GCTScript { + if m != nil { + return m.Script } return nil } type GCTScriptAutoLoadRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Script string `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` - Status bool `protobuf:"varint,2,opt,name=status,proto3" json:"status,omitempty"` + Script string `protobuf:"bytes,1,opt,name=script,proto3" json:"script,omitempty"` + Status bool `protobuf:"varint,2,opt,name=status,proto3" json:"status,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptAutoLoadRequest) Reset() { - *x = GCTScriptAutoLoadRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[123] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptAutoLoadRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptAutoLoadRequest) ProtoMessage() {} - -func (x *GCTScriptAutoLoadRequest) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[123] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptAutoLoadRequest.ProtoReflect.Descriptor instead. +func (m *GCTScriptAutoLoadRequest) Reset() { *m = GCTScriptAutoLoadRequest{} } +func (m *GCTScriptAutoLoadRequest) String() string { return proto.CompactTextString(m) } +func (*GCTScriptAutoLoadRequest) ProtoMessage() {} func (*GCTScriptAutoLoadRequest) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{123} + return fileDescriptor_77a6da22d6a3feb1, []int{117} } -func (x *GCTScriptAutoLoadRequest) GetScript() string { - if x != nil { - return x.Script +func (m *GCTScriptAutoLoadRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptAutoLoadRequest.Unmarshal(m, b) +} +func (m *GCTScriptAutoLoadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptAutoLoadRequest.Marshal(b, m, deterministic) +} +func (m *GCTScriptAutoLoadRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptAutoLoadRequest.Merge(m, src) +} +func (m *GCTScriptAutoLoadRequest) XXX_Size() int { + return xxx_messageInfo_GCTScriptAutoLoadRequest.Size(m) +} +func (m *GCTScriptAutoLoadRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptAutoLoadRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptAutoLoadRequest proto.InternalMessageInfo + +func (m *GCTScriptAutoLoadRequest) GetScript() string { + if m != nil { + return m.Script } return "" } -func (x *GCTScriptAutoLoadRequest) GetStatus() bool { - if x != nil { - return x.Status +func (m *GCTScriptAutoLoadRequest) GetStatus() bool { + if m != nil { + return m.Status } return false } type GCTScriptStatusResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Scripts []*GCTScript `protobuf:"bytes,2,rep,name=scripts,proto3" json:"scripts,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Scripts []*GCTScript `protobuf:"bytes,2,rep,name=scripts,proto3" json:"scripts,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptStatusResponse) Reset() { - *x = GCTScriptStatusResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[124] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptStatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptStatusResponse) ProtoMessage() {} - -func (x *GCTScriptStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[124] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptStatusResponse.ProtoReflect.Descriptor instead. +func (m *GCTScriptStatusResponse) Reset() { *m = GCTScriptStatusResponse{} } +func (m *GCTScriptStatusResponse) String() string { return proto.CompactTextString(m) } +func (*GCTScriptStatusResponse) ProtoMessage() {} func (*GCTScriptStatusResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{124} + return fileDescriptor_77a6da22d6a3feb1, []int{118} } -func (x *GCTScriptStatusResponse) GetStatus() string { - if x != nil { - return x.Status +func (m *GCTScriptStatusResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptStatusResponse.Unmarshal(m, b) +} +func (m *GCTScriptStatusResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptStatusResponse.Marshal(b, m, deterministic) +} +func (m *GCTScriptStatusResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptStatusResponse.Merge(m, src) +} +func (m *GCTScriptStatusResponse) XXX_Size() int { + return xxx_messageInfo_GCTScriptStatusResponse.Size(m) +} +func (m *GCTScriptStatusResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptStatusResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptStatusResponse proto.InternalMessageInfo + +func (m *GCTScriptStatusResponse) GetStatus() string { + if m != nil { + return m.Status } return "" } -func (x *GCTScriptStatusResponse) GetScripts() []*GCTScript { - if x != nil { - return x.Scripts +func (m *GCTScriptStatusResponse) GetScripts() []*GCTScript { + if m != nil { + return m.Scripts } return nil } type GCTScriptQueryResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Script *GCTScript `protobuf:"bytes,2,opt,name=script,proto3" json:"script,omitempty"` - Data string `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Script *GCTScript `protobuf:"bytes,2,opt,name=script,proto3" json:"script,omitempty"` + Data string `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptQueryResponse) Reset() { - *x = GCTScriptQueryResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[125] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GCTScriptQueryResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GCTScriptQueryResponse) ProtoMessage() {} - -func (x *GCTScriptQueryResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[125] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptQueryResponse.ProtoReflect.Descriptor instead. +func (m *GCTScriptQueryResponse) Reset() { *m = GCTScriptQueryResponse{} } +func (m *GCTScriptQueryResponse) String() string { return proto.CompactTextString(m) } +func (*GCTScriptQueryResponse) ProtoMessage() {} func (*GCTScriptQueryResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{125} + return fileDescriptor_77a6da22d6a3feb1, []int{119} } -func (x *GCTScriptQueryResponse) GetStatus() string { - if x != nil { - return x.Status +func (m *GCTScriptQueryResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GCTScriptQueryResponse.Unmarshal(m, b) +} +func (m *GCTScriptQueryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GCTScriptQueryResponse.Marshal(b, m, deterministic) +} +func (m *GCTScriptQueryResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GCTScriptQueryResponse.Merge(m, src) +} +func (m *GCTScriptQueryResponse) XXX_Size() int { + return xxx_messageInfo_GCTScriptQueryResponse.Size(m) +} +func (m *GCTScriptQueryResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GCTScriptQueryResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GCTScriptQueryResponse proto.InternalMessageInfo + +func (m *GCTScriptQueryResponse) GetStatus() string { + if m != nil { + return m.Status } return "" } -func (x *GCTScriptQueryResponse) GetScript() *GCTScript { - if x != nil { - return x.Script +func (m *GCTScriptQueryResponse) GetScript() *GCTScript { + if m != nil { + return m.Script } return nil } -func (x *GCTScriptQueryResponse) GetData() string { - if x != nil { - return x.Data +func (m *GCTScriptQueryResponse) GetData() string { + if m != nil { + return m.Data } return "" } -type GCTScriptGenericResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +type GenericResponse struct { + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *GCTScriptGenericResponse) Reset() { - *x = GCTScriptGenericResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[126] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *GenericResponse) Reset() { *m = GenericResponse{} } +func (m *GenericResponse) String() string { return proto.CompactTextString(m) } +func (*GenericResponse) ProtoMessage() {} +func (*GenericResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{120} } -func (x *GCTScriptGenericResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *GenericResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GenericResponse.Unmarshal(m, b) +} +func (m *GenericResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GenericResponse.Marshal(b, m, deterministic) +} +func (m *GenericResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenericResponse.Merge(m, src) +} +func (m *GenericResponse) XXX_Size() int { + return xxx_messageInfo_GenericResponse.Size(m) +} +func (m *GenericResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GenericResponse.DiscardUnknown(m) } -func (*GCTScriptGenericResponse) ProtoMessage() {} +var xxx_messageInfo_GenericResponse proto.InternalMessageInfo -func (x *GCTScriptGenericResponse) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[126] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GCTScriptGenericResponse.ProtoReflect.Descriptor instead. -func (*GCTScriptGenericResponse) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{126} -} - -func (x *GCTScriptGenericResponse) GetStatus() string { - if x != nil { - return x.Status +func (m *GenericResponse) GetStatus() string { + if m != nil { + return m.Status } return "" } -func (x *GCTScriptGenericResponse) GetData() string { - if x != nil { - return x.Data +func (m *GenericResponse) GetData() string { + if m != nil { + return m.Data } return "" } -type CancelAllOrdersResponse_Orders struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` - OrderStatus map[string]string `protobuf:"bytes,2,rep,name=order_status,json=orderStatus,proto3" json:"order_status,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +type SetExchangeAssetRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Asset string `protobuf:"bytes,2,opt,name=asset,proto3" json:"asset,omitempty"` + Enable bool `protobuf:"varint,3,opt,name=enable,proto3" json:"enable,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (x *CancelAllOrdersResponse_Orders) Reset() { - *x = CancelAllOrdersResponse_Orders{} - if protoimpl.UnsafeEnabled { - mi := &file_rpc_proto_msgTypes[137] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (m *SetExchangeAssetRequest) Reset() { *m = SetExchangeAssetRequest{} } +func (m *SetExchangeAssetRequest) String() string { return proto.CompactTextString(m) } +func (*SetExchangeAssetRequest) ProtoMessage() {} +func (*SetExchangeAssetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{121} } -func (x *CancelAllOrdersResponse_Orders) String() string { - return protoimpl.X.MessageStringOf(x) +func (m *SetExchangeAssetRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SetExchangeAssetRequest.Unmarshal(m, b) +} +func (m *SetExchangeAssetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SetExchangeAssetRequest.Marshal(b, m, deterministic) +} +func (m *SetExchangeAssetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetExchangeAssetRequest.Merge(m, src) +} +func (m *SetExchangeAssetRequest) XXX_Size() int { + return xxx_messageInfo_SetExchangeAssetRequest.Size(m) +} +func (m *SetExchangeAssetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SetExchangeAssetRequest.DiscardUnknown(m) } -func (*CancelAllOrdersResponse_Orders) ProtoMessage() {} +var xxx_messageInfo_SetExchangeAssetRequest proto.InternalMessageInfo -func (x *CancelAllOrdersResponse_Orders) ProtoReflect() protoreflect.Message { - mi := &file_rpc_proto_msgTypes[137] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CancelAllOrdersResponse_Orders.ProtoReflect.Descriptor instead. -func (*CancelAllOrdersResponse_Orders) Descriptor() ([]byte, []int) { - return file_rpc_proto_rawDescGZIP(), []int{73, 0} -} - -func (x *CancelAllOrdersResponse_Orders) GetExchange() string { - if x != nil { - return x.Exchange +func (m *SetExchangeAssetRequest) GetExchange() string { + if m != nil { + return m.Exchange } return "" } -func (x *CancelAllOrdersResponse_Orders) GetOrderStatus() map[string]string { - if x != nil { - return x.OrderStatus +func (m *SetExchangeAssetRequest) GetAsset() string { + if m != nil { + return m.Asset + } + return "" +} + +func (m *SetExchangeAssetRequest) GetEnable() bool { + if m != nil { + return m.Enable + } + return false +} + +type SetExchangeAllPairsRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Enable bool `protobuf:"varint,2,opt,name=enable,proto3" json:"enable,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SetExchangeAllPairsRequest) Reset() { *m = SetExchangeAllPairsRequest{} } +func (m *SetExchangeAllPairsRequest) String() string { return proto.CompactTextString(m) } +func (*SetExchangeAllPairsRequest) ProtoMessage() {} +func (*SetExchangeAllPairsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{122} +} + +func (m *SetExchangeAllPairsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SetExchangeAllPairsRequest.Unmarshal(m, b) +} +func (m *SetExchangeAllPairsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SetExchangeAllPairsRequest.Marshal(b, m, deterministic) +} +func (m *SetExchangeAllPairsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SetExchangeAllPairsRequest.Merge(m, src) +} +func (m *SetExchangeAllPairsRequest) XXX_Size() int { + return xxx_messageInfo_SetExchangeAllPairsRequest.Size(m) +} +func (m *SetExchangeAllPairsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SetExchangeAllPairsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SetExchangeAllPairsRequest proto.InternalMessageInfo + +func (m *SetExchangeAllPairsRequest) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +func (m *SetExchangeAllPairsRequest) GetEnable() bool { + if m != nil { + return m.Enable + } + return false +} + +type UpdateExchangeSupportedPairsRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *UpdateExchangeSupportedPairsRequest) Reset() { *m = UpdateExchangeSupportedPairsRequest{} } +func (m *UpdateExchangeSupportedPairsRequest) String() string { return proto.CompactTextString(m) } +func (*UpdateExchangeSupportedPairsRequest) ProtoMessage() {} +func (*UpdateExchangeSupportedPairsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{123} +} + +func (m *UpdateExchangeSupportedPairsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_UpdateExchangeSupportedPairsRequest.Unmarshal(m, b) +} +func (m *UpdateExchangeSupportedPairsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_UpdateExchangeSupportedPairsRequest.Marshal(b, m, deterministic) +} +func (m *UpdateExchangeSupportedPairsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateExchangeSupportedPairsRequest.Merge(m, src) +} +func (m *UpdateExchangeSupportedPairsRequest) XXX_Size() int { + return xxx_messageInfo_UpdateExchangeSupportedPairsRequest.Size(m) +} +func (m *UpdateExchangeSupportedPairsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateExchangeSupportedPairsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateExchangeSupportedPairsRequest proto.InternalMessageInfo + +func (m *UpdateExchangeSupportedPairsRequest) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +type GetExchangeAssetsRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetExchangeAssetsRequest) Reset() { *m = GetExchangeAssetsRequest{} } +func (m *GetExchangeAssetsRequest) String() string { return proto.CompactTextString(m) } +func (*GetExchangeAssetsRequest) ProtoMessage() {} +func (*GetExchangeAssetsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{124} +} + +func (m *GetExchangeAssetsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeAssetsRequest.Unmarshal(m, b) +} +func (m *GetExchangeAssetsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeAssetsRequest.Marshal(b, m, deterministic) +} +func (m *GetExchangeAssetsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeAssetsRequest.Merge(m, src) +} +func (m *GetExchangeAssetsRequest) XXX_Size() int { + return xxx_messageInfo_GetExchangeAssetsRequest.Size(m) +} +func (m *GetExchangeAssetsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeAssetsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeAssetsRequest proto.InternalMessageInfo + +func (m *GetExchangeAssetsRequest) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +type GetExchangeAssetsResponse struct { + Assets string `protobuf:"bytes,1,opt,name=assets,proto3" json:"assets,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetExchangeAssetsResponse) Reset() { *m = GetExchangeAssetsResponse{} } +func (m *GetExchangeAssetsResponse) String() string { return proto.CompactTextString(m) } +func (*GetExchangeAssetsResponse) ProtoMessage() {} +func (*GetExchangeAssetsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{125} +} + +func (m *GetExchangeAssetsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetExchangeAssetsResponse.Unmarshal(m, b) +} +func (m *GetExchangeAssetsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetExchangeAssetsResponse.Marshal(b, m, deterministic) +} +func (m *GetExchangeAssetsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetExchangeAssetsResponse.Merge(m, src) +} +func (m *GetExchangeAssetsResponse) XXX_Size() int { + return xxx_messageInfo_GetExchangeAssetsResponse.Size(m) +} +func (m *GetExchangeAssetsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetExchangeAssetsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetExchangeAssetsResponse proto.InternalMessageInfo + +func (m *GetExchangeAssetsResponse) GetAssets() string { + if m != nil { + return m.Assets + } + return "" +} + +type WebsocketGetInfoRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WebsocketGetInfoRequest) Reset() { *m = WebsocketGetInfoRequest{} } +func (m *WebsocketGetInfoRequest) String() string { return proto.CompactTextString(m) } +func (*WebsocketGetInfoRequest) ProtoMessage() {} +func (*WebsocketGetInfoRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{126} +} + +func (m *WebsocketGetInfoRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WebsocketGetInfoRequest.Unmarshal(m, b) +} +func (m *WebsocketGetInfoRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WebsocketGetInfoRequest.Marshal(b, m, deterministic) +} +func (m *WebsocketGetInfoRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WebsocketGetInfoRequest.Merge(m, src) +} +func (m *WebsocketGetInfoRequest) XXX_Size() int { + return xxx_messageInfo_WebsocketGetInfoRequest.Size(m) +} +func (m *WebsocketGetInfoRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WebsocketGetInfoRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WebsocketGetInfoRequest proto.InternalMessageInfo + +func (m *WebsocketGetInfoRequest) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +type WebsocketGetInfoResponse struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Supported bool `protobuf:"varint,2,opt,name=supported,proto3" json:"supported,omitempty"` + Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"` + AuthenticatedSupported bool `protobuf:"varint,4,opt,name=authenticated_supported,json=authenticatedSupported,proto3" json:"authenticated_supported,omitempty"` + Authenticated bool `protobuf:"varint,5,opt,name=authenticated,proto3" json:"authenticated,omitempty"` + RunningUrl string `protobuf:"bytes,6,opt,name=running_url,json=runningUrl,proto3" json:"running_url,omitempty"` + ProxyAddress string `protobuf:"bytes,7,opt,name=proxy_address,json=proxyAddress,proto3" json:"proxy_address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WebsocketGetInfoResponse) Reset() { *m = WebsocketGetInfoResponse{} } +func (m *WebsocketGetInfoResponse) String() string { return proto.CompactTextString(m) } +func (*WebsocketGetInfoResponse) ProtoMessage() {} +func (*WebsocketGetInfoResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{127} +} + +func (m *WebsocketGetInfoResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WebsocketGetInfoResponse.Unmarshal(m, b) +} +func (m *WebsocketGetInfoResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WebsocketGetInfoResponse.Marshal(b, m, deterministic) +} +func (m *WebsocketGetInfoResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WebsocketGetInfoResponse.Merge(m, src) +} +func (m *WebsocketGetInfoResponse) XXX_Size() int { + return xxx_messageInfo_WebsocketGetInfoResponse.Size(m) +} +func (m *WebsocketGetInfoResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WebsocketGetInfoResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_WebsocketGetInfoResponse proto.InternalMessageInfo + +func (m *WebsocketGetInfoResponse) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +func (m *WebsocketGetInfoResponse) GetSupported() bool { + if m != nil { + return m.Supported + } + return false +} + +func (m *WebsocketGetInfoResponse) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *WebsocketGetInfoResponse) GetAuthenticatedSupported() bool { + if m != nil { + return m.AuthenticatedSupported + } + return false +} + +func (m *WebsocketGetInfoResponse) GetAuthenticated() bool { + if m != nil { + return m.Authenticated + } + return false +} + +func (m *WebsocketGetInfoResponse) GetRunningUrl() string { + if m != nil { + return m.RunningUrl + } + return "" +} + +func (m *WebsocketGetInfoResponse) GetProxyAddress() string { + if m != nil { + return m.ProxyAddress + } + return "" +} + +type WebsocketSetEnabledRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Enable bool `protobuf:"varint,2,opt,name=enable,proto3" json:"enable,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WebsocketSetEnabledRequest) Reset() { *m = WebsocketSetEnabledRequest{} } +func (m *WebsocketSetEnabledRequest) String() string { return proto.CompactTextString(m) } +func (*WebsocketSetEnabledRequest) ProtoMessage() {} +func (*WebsocketSetEnabledRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{128} +} + +func (m *WebsocketSetEnabledRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WebsocketSetEnabledRequest.Unmarshal(m, b) +} +func (m *WebsocketSetEnabledRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WebsocketSetEnabledRequest.Marshal(b, m, deterministic) +} +func (m *WebsocketSetEnabledRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WebsocketSetEnabledRequest.Merge(m, src) +} +func (m *WebsocketSetEnabledRequest) XXX_Size() int { + return xxx_messageInfo_WebsocketSetEnabledRequest.Size(m) +} +func (m *WebsocketSetEnabledRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WebsocketSetEnabledRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WebsocketSetEnabledRequest proto.InternalMessageInfo + +func (m *WebsocketSetEnabledRequest) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +func (m *WebsocketSetEnabledRequest) GetEnable() bool { + if m != nil { + return m.Enable + } + return false +} + +type WebsocketGetSubscriptionsRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WebsocketGetSubscriptionsRequest) Reset() { *m = WebsocketGetSubscriptionsRequest{} } +func (m *WebsocketGetSubscriptionsRequest) String() string { return proto.CompactTextString(m) } +func (*WebsocketGetSubscriptionsRequest) ProtoMessage() {} +func (*WebsocketGetSubscriptionsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{129} +} + +func (m *WebsocketGetSubscriptionsRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WebsocketGetSubscriptionsRequest.Unmarshal(m, b) +} +func (m *WebsocketGetSubscriptionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WebsocketGetSubscriptionsRequest.Marshal(b, m, deterministic) +} +func (m *WebsocketGetSubscriptionsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WebsocketGetSubscriptionsRequest.Merge(m, src) +} +func (m *WebsocketGetSubscriptionsRequest) XXX_Size() int { + return xxx_messageInfo_WebsocketGetSubscriptionsRequest.Size(m) +} +func (m *WebsocketGetSubscriptionsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WebsocketGetSubscriptionsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WebsocketGetSubscriptionsRequest proto.InternalMessageInfo + +func (m *WebsocketGetSubscriptionsRequest) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +type WebsocketSubscription struct { + Channel string `protobuf:"bytes,1,opt,name=channel,proto3" json:"channel,omitempty"` + Currency string `protobuf:"bytes,2,opt,name=currency,proto3" json:"currency,omitempty"` + Asset string `protobuf:"bytes,3,opt,name=asset,proto3" json:"asset,omitempty"` + Params string `protobuf:"bytes,4,opt,name=params,proto3" json:"params,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WebsocketSubscription) Reset() { *m = WebsocketSubscription{} } +func (m *WebsocketSubscription) String() string { return proto.CompactTextString(m) } +func (*WebsocketSubscription) ProtoMessage() {} +func (*WebsocketSubscription) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{130} +} + +func (m *WebsocketSubscription) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WebsocketSubscription.Unmarshal(m, b) +} +func (m *WebsocketSubscription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WebsocketSubscription.Marshal(b, m, deterministic) +} +func (m *WebsocketSubscription) XXX_Merge(src proto.Message) { + xxx_messageInfo_WebsocketSubscription.Merge(m, src) +} +func (m *WebsocketSubscription) XXX_Size() int { + return xxx_messageInfo_WebsocketSubscription.Size(m) +} +func (m *WebsocketSubscription) XXX_DiscardUnknown() { + xxx_messageInfo_WebsocketSubscription.DiscardUnknown(m) +} + +var xxx_messageInfo_WebsocketSubscription proto.InternalMessageInfo + +func (m *WebsocketSubscription) GetChannel() string { + if m != nil { + return m.Channel + } + return "" +} + +func (m *WebsocketSubscription) GetCurrency() string { + if m != nil { + return m.Currency + } + return "" +} + +func (m *WebsocketSubscription) GetAsset() string { + if m != nil { + return m.Asset + } + return "" +} + +func (m *WebsocketSubscription) GetParams() string { + if m != nil { + return m.Params + } + return "" +} + +type WebsocketGetSubscriptionsResponse struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Subscriptions []*WebsocketSubscription `protobuf:"bytes,2,rep,name=subscriptions,proto3" json:"subscriptions,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WebsocketGetSubscriptionsResponse) Reset() { *m = WebsocketGetSubscriptionsResponse{} } +func (m *WebsocketGetSubscriptionsResponse) String() string { return proto.CompactTextString(m) } +func (*WebsocketGetSubscriptionsResponse) ProtoMessage() {} +func (*WebsocketGetSubscriptionsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{131} +} + +func (m *WebsocketGetSubscriptionsResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WebsocketGetSubscriptionsResponse.Unmarshal(m, b) +} +func (m *WebsocketGetSubscriptionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WebsocketGetSubscriptionsResponse.Marshal(b, m, deterministic) +} +func (m *WebsocketGetSubscriptionsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_WebsocketGetSubscriptionsResponse.Merge(m, src) +} +func (m *WebsocketGetSubscriptionsResponse) XXX_Size() int { + return xxx_messageInfo_WebsocketGetSubscriptionsResponse.Size(m) +} +func (m *WebsocketGetSubscriptionsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_WebsocketGetSubscriptionsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_WebsocketGetSubscriptionsResponse proto.InternalMessageInfo + +func (m *WebsocketGetSubscriptionsResponse) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +func (m *WebsocketGetSubscriptionsResponse) GetSubscriptions() []*WebsocketSubscription { + if m != nil { + return m.Subscriptions } return nil } -var File_rpc_proto protoreflect.FileDescriptor - -var file_rpc_proto_rawDesc = []byte{ - 0x0a, 0x09, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0xb4, 0x04, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x70, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x2f, 0x0a, 0x13, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x61, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x34, - 0x0a, 0x16, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, - 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, - 0x66, 0x69, 0x61, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x13, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x46, 0x69, 0x61, 0x74, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x57, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0f, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x72, 0x70, 0x63, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x52, 0x70, 0x63, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0c, 0x72, 0x70, 0x63, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x73, 0x1a, 0x42, 0x0a, 0x14, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x54, 0x0a, 0x11, 0x52, 0x70, 0x63, 0x45, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x21, 0x0a, 0x1f, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4e, - 0x0a, 0x14, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x86, - 0x02, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6f, - 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x15, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x1a, - 0x66, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x37, 0x0a, 0x17, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, - 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0xbe, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, - 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, - 0x0a, 0x11, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x10, - 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x1a, 0x43, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x4e, 0x0a, 0x0b, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, - 0xba, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x65, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, - 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x1a, 0x51, 0x0a, 0x0e, 0x45, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x38, 0x0a, 0x1a, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x1d, 0x0a, 0x1b, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, - 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x34, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0x32, 0x0a, 0x15, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x64, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x74, 0x70, 0x43, 0x6f, 0x64, 0x65, - 0x22, 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, - 0x54, 0x50, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa2, 0x01, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x09, 0x6f, 0x74, 0x70, 0x5f, 0x63, 0x6f, - 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, - 0x50, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x74, 0x70, 0x43, 0x6f, - 0x64, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6f, 0x74, 0x70, 0x43, 0x6f, 0x64, - 0x65, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x4f, 0x74, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x34, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x5e, 0x0a, 0x0e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x53, 0x75, - 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x69, 0x72, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x50, 0x61, 0x69, 0x72, 0x73, 0x22, 0x82, 0x04, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x69, - 0x6e, 0x67, 0x5f, 0x73, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0c, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x53, 0x61, 0x6e, 0x64, 0x62, 0x6f, 0x78, 0x12, 0x21, - 0x0a, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x74, 0x74, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x68, 0x74, 0x74, 0x70, 0x55, - 0x73, 0x65, 0x72, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x74, 0x70, - 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x68, 0x74, - 0x74, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x62, 0x61, 0x73, 0x65, 0x5f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x62, 0x61, 0x73, 0x65, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, - 0x12, 0x5f, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x61, 0x70, 0x69, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x61, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x41, 0x70, 0x69, 0x1a, 0x5a, - 0x0a, 0x14, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x77, 0x0a, 0x10, 0x47, 0x65, - 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, - 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, - 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x22, 0x56, 0x0a, 0x0c, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, - 0x61, 0x69, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x22, 0x95, 0x02, 0x0a, 0x0e, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, - 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, - 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, - 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, - 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x61, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, - 0x6c, 0x61, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x01, 0x52, 0x04, 0x68, 0x69, 0x67, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x6f, 0x77, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6c, 0x6f, 0x77, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x69, - 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x62, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, - 0x61, 0x73, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x61, 0x73, 0x6b, 0x12, 0x16, - 0x0a, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, - 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, - 0x61, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x70, 0x72, 0x69, 0x63, 0x65, - 0x41, 0x74, 0x68, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x07, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x30, 0x0a, 0x07, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x73, 0x22, 0x3f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x07, 0x74, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x07, 0x74, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x73, 0x22, 0x7a, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, - 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, - 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x4d, - 0x0a, 0x0d, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x12, - 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0xfa, 0x01, - 0x0a, 0x11, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x23, 0x0a, - 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, - 0x69, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x62, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x62, 0x69, 0x64, 0x73, 0x12, 0x29, 0x0a, - 0x04, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x49, 0x74, - 0x65, 0x6d, 0x52, 0x04, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, - 0x6c, 0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x63, 0x0a, 0x0a, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x0a, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x22, 0x4b, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x32, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x73, 0x22, 0x33, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x56, 0x0a, 0x07, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x69, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x69, 0x65, - 0x73, 0x22, 0x66, 0x0a, 0x13, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x04, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x61, 0x0a, 0x16, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x2b, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x12, 0x0a, 0x10, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x27, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x85, 0x01, 0x0a, 0x10, 0x50, 0x6f, - 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, - 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x69, - 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x22, 0x15, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x36, 0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x09, 0x70, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x22, 0x1c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x6e, 0x0a, 0x04, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x12, - 0x0a, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x6f, - 0x69, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, - 0x74, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, - 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, 0x68, 0x0a, 0x12, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, - 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, - 0x22, 0x4d, 0x0a, 0x11, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, - 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x22, - 0x48, 0x0a, 0x0c, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, - 0x38, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x66, 0x66, 0x6c, - 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x09, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x0b, 0x4f, 0x6e, - 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x12, 0x34, 0x0a, 0x05, 0x63, 0x6f, 0x69, - 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x2e, 0x43, 0x6f, - 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x1a, - 0x53, 0x0a, 0x0a, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, - 0x69, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xcb, 0x04, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x0b, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x74, - 0x61, 0x6c, 0x73, 0x12, 0x31, 0x0a, 0x0d, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x5f, 0x6f, 0x66, 0x66, - 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0c, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x4f, - 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x70, 0x0a, 0x15, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x5f, - 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, - 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x73, - 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x13, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, - 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x2f, 0x0a, 0x0c, 0x63, 0x6f, 0x69, 0x6e, - 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x0b, 0x63, 0x6f, - 0x69, 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x6d, 0x0a, 0x14, 0x63, 0x6f, 0x69, - 0x6e, 0x73, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x69, - 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x1a, 0x5c, 0x0a, 0x18, 0x43, 0x6f, 0x69, 0x6e, - 0x73, 0x4f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, - 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5a, 0x0a, 0x17, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x4f, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0xe3, 0x01, 0x0a, 0x1a, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, - 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x62, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x13, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x64, 0x5f, 0x73, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x63, 0x6f, 0x6c, - 0x64, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x1d, 0x0a, 0x1b, 0x41, 0x64, 0x64, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x1d, 0x52, 0x65, 0x6d, 0x6f, 0x76, - 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x20, 0x0a, 0x1e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xed, 0x01, 0x0a, 0x0d, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x73, - 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x72, 0x65, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x6c, 0x69, - 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x61, 0x70, 0x69, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, - 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, - 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, - 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, - 0x5b, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0f, - 0x66, 0x6f, 0x72, 0x65, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x0e, 0x66, 0x6f, - 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x16, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x71, 0x0a, 0x14, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, - 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, - 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, - 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, - 0x12, 0x12, 0x0a, 0x04, 0x72, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, - 0x72, 0x61, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, - 0x72, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x69, 0x6e, 0x76, 0x65, - 0x72, 0x73, 0x65, 0x52, 0x61, 0x74, 0x65, 0x22, 0x56, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x46, 0x6f, - 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x3d, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, - 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x22, - 0xaf, 0x03, 0x0a, 0x0c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, - 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x61, 0x73, 0x65, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x71, 0x75, 0x6f, 0x74, 0x65, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x53, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x6e, 0x56, 0x6f, 0x6c, 0x75, 0x6d, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, - 0x66, 0x65, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, 0x73, 0x18, 0x0e, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, - 0x64, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x06, 0x74, 0x72, 0x61, 0x64, 0x65, - 0x73, 0x22, 0xdd, 0x01, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, - 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x69, 0x64, 0x65, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x69, 0x64, 0x65, 0x12, - 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, - 0x65, 0x22, 0x77, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x22, 0x41, 0x0a, 0x11, 0x47, 0x65, - 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2c, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0x48, 0x0a, - 0x0f, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x22, 0xd8, 0x01, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, - 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, - 0x70, 0x61, 0x69, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, - 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x49, 0x64, 0x22, 0x53, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x5f, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x22, 0x88, 0x01, 0x0a, 0x14, 0x53, 0x69, 0x6d, 0x75, - 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, - 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, - 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, - 0x64, 0x65, 0x22, 0xf2, 0x01, 0x0a, 0x15, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x49, - 0x74, 0x65, 0x6d, 0x52, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, - 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x70, - 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x69, - 0x6d, 0x75, 0x6d, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x69, - 0x6d, 0x75, 0x6d, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x0c, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x30, 0x0a, - 0x14, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x67, 0x61, 0x69, 0x6e, - 0x5f, 0x6c, 0x6f, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x12, 0x70, 0x65, 0x72, - 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x47, 0x61, 0x69, 0x6e, 0x4c, 0x6f, 0x73, 0x73, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x10, 0x57, 0x68, 0x61, 0x6c, - 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, - 0x69, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x69, 0x63, 0x65, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x22, 0xee, 0x01, 0x0a, 0x12, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, - 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x25, 0x0a, 0x0e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x34, 0x0a, 0x16, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x9c, 0x02, 0x0a, 0x17, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x06, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x06, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x73, 0x1a, 0xc0, 0x01, 0x0a, 0x06, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1a, - 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x5a, 0x0a, 0x0c, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x37, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbe, 0x01, 0x0a, 0x0f, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1c, - 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, - 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x70, 0x72, 0x69, - 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x62, 0x69, 0x64, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x69, 0x64, - 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x5f, 0x62, 0x69, 0x64, 0x73, 0x5f, - 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x69, 0x64, 0x73, 0x41, 0x6e, 0x64, 0x41, 0x73, 0x6b, 0x73, - 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x5f, 0x61, 0x6d, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xf5, 0x01, 0x0a, 0x11, - 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x74, 0x65, - 0x6d, 0x12, 0x42, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, - 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x64, 0x22, 0xe6, 0x01, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x42, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x70, - 0x61, 0x69, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x22, 0x0a, 0x10, - 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, - 0x22, 0x24, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x46, 0x0a, - 0x28, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xc9, 0x01, 0x0a, 0x29, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x6c, 0x0a, 0x26, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x22, - 0x43, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x22, 0xaf, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, - 0x0a, 0x0f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x62, 0x61, 0x6e, 0x6b, 0x41, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xd6, 0x01, 0x0a, 0x15, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x66, - 0x65, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x20, 0x0a, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0x3a, 0x0a, 0x10, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x2c, 0x0a, 0x1a, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, - 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x54, 0x0a, 0x1b, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, - 0x65, 0x0a, 0x21, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x79, 0x0a, 0x1d, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x44, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x22, 0x5b, 0x0a, 0x22, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x95, - 0x02, 0x0a, 0x17, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x6c, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x54, 0x0a, 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x6c, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0xea, 0x01, 0x0a, - 0x16, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x66, 0x69, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x46, 0x69, 0x61, 0x74, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x04, 0x66, 0x69, - 0x61, 0x74, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x22, 0xb8, 0x01, 0x0a, 0x13, 0x46, 0x69, - 0x61, 0x74, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x6e, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x62, 0x73, 0x62, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x62, 0x73, 0x62, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x77, - 0x69, 0x66, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x77, 0x69, 0x66, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x69, 0x62, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x69, 0x62, 0x61, 0x6e, 0x22, 0x64, 0x0a, 0x15, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x61, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x66, 0x65, 0x65, 0x22, 0x31, 0x0a, 0x17, 0x47, 0x65, - 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x22, 0x6e, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x6e, 0x66, - 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, - 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, - 0x62, 0x75, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x04, 0x77, 0x61, 0x72, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x47, 0x0a, - 0x17, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x67, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x4b, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x22, 0xd8, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x60, 0x0a, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, - 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x1a, 0x5a, 0x0a, 0x14, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, - 0x74, 0x65, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7a, - 0x0a, 0x13, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x22, 0x80, 0x01, 0x0a, 0x19, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, - 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3f, 0x0a, - 0x21, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x7d, - 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, 0x12, 0x1d, - 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3c, 0x0a, - 0x1e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x14, - 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, 0x61, - 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x44, - 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, - 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, - 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, - 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x43, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x75, - 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x2a, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xec, 0x01, 0x0a, - 0x19, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, - 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x5f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, - 0x74, 0x69, 0x6d, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, - 0x65, 0x78, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xce, 0x01, 0x0a, 0x1a, - 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x70, 0x61, 0x69, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x50, 0x61, 0x69, 0x72, 0x52, 0x04, 0x70, 0x61, 0x69, 0x72, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, - 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x84, 0x01, 0x0a, - 0x06, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6c, - 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x03, 0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, - 0x04, 0x68, 0x69, 0x67, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x04, 0x68, 0x69, 0x67, - 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, - 0x04, 0x6f, 0x70, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x76, - 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x06, 0x76, 0x6f, 0x6c, - 0x75, 0x6d, 0x65, 0x22, 0x78, 0x0a, 0x0a, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x62, 0x0a, - 0x09, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x55, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x55, 0x49, 0x44, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x72, - 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x78, 0x74, 0x52, 0x75, - 0x6e, 0x22, 0x44, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, - 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x41, 0x0a, 0x14, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x43, - 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x18, 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x19, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, - 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa8, 0x01, 0x0a, 0x16, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x61, - 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, - 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, - 0x72, 0x69, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, 0x72, - 0x77, 0x72, 0x69, 0x74, 0x65, 0x22, 0x47, 0x0a, 0x1a, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x42, - 0x0a, 0x15, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x22, 0x4a, 0x0a, 0x18, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, - 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x5e, - 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x22, 0x6f, - 0x0a, 0x16, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x29, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, - 0x46, 0x0a, 0x18, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0xcd, 0x38, 0x0a, 0x0e, 0x47, 0x6f, 0x43, 0x72, - 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x07, 0x47, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x67, 0x0a, 0x0d, 0x47, - 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1c, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x73, 0x62, 0x73, 0x79, 0x74, 0x65, 0x6d, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x73, 0x12, 0x71, 0x0a, 0x0f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x75, - 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, - 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, - 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x75, - 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x73, 0x0a, 0x10, 0x44, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x53, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, 0x73, - 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x75, 0x62, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x6f, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x50, 0x43, 0x45, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, - 0x74, 0x72, 0x70, 0x63, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x93, 0x01, - 0x0a, 0x18, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x12, 0x27, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x12, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, - 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x72, 0x65, 0x6c, 0x61, 0x79, - 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x7a, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x22, 0x13, 0x2f, 0x76, - 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x73, 0x0a, 0x12, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x43, 0x6f, 0x64, 0x65, 0x12, - 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x52, 0x65, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, 0x74, 0x70, 0x12, 0x73, - 0x0a, 0x13, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, - 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x54, 0x50, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, - 0x74, 0x70, 0x73, 0x12, 0x78, 0x0a, 0x0e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, - 0x09, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, - 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x73, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, - 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x15, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x3a, 0x01, 0x2a, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, - 0x73, 0x12, 0x6b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x12, 0x12, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x79, - 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x69, 0x6e, 0x66, - 0x6f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, - 0x69, 0x6f, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, - 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x12, 0x7f, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, - 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x82, 0x01, 0x0a, 0x13, 0x41, 0x64, 0x64, - 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6f, 0x72, - 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, - 0x64, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, 0x64, 0x70, 0x6f, 0x72, 0x74, 0x66, 0x6f, - 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8e, 0x01, - 0x0a, 0x16, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, - 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x6f, 0x6c, 0x69, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, - 0x1a, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x66, - 0x6f, 0x6c, 0x69, 0x6f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x77, - 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x67, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6f, - 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x46, 0x6f, 0x72, 0x65, 0x78, 0x52, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x66, 0x6f, 0x72, 0x65, 0x78, 0x72, 0x61, 0x74, 0x65, 0x73, - 0x12, 0x5a, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x18, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x52, 0x0a, 0x08, - 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, - 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, - 0x12, 0x62, 0x0a, 0x0b, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, - 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0d, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, - 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, - 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x69, 0x6d, - 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, - 0x73, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x01, 0x2a, - 0x12, 0x5e, 0x0a, 0x09, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, 0x12, 0x18, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x68, 0x61, 0x6c, 0x65, 0x42, 0x6f, 0x6d, 0x62, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x69, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x68, 0x61, 0x6c, 0x65, 0x62, 0x6f, 0x6d, 0x62, 0x3a, 0x01, 0x2a, - 0x12, 0x62, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, - 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x72, 0x0a, 0x0f, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, - 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x41, 0x6c, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, - 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x61, 0x6c, 0x6c, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x57, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x12, 0x56, 0x0a, 0x08, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x41, 0x64, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x64, - 0x64, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x62, 0x0a, 0x0b, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0xb2, 0x01, - 0x0a, 0x21, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, - 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, - 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x3a, - 0x01, 0x2a, 0x12, 0xaa, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, - 0x79, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x22, - 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x01, 0x2a, 0x12, - 0x6c, 0x0a, 0x11, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x46, - 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, - 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x46, 0x69, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x66, 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x8b, 0x01, - 0x0a, 0x1b, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x46, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1d, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x43, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x33, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x22, 0x28, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x69, 0x74, 0x68, 0x64, - 0x72, 0x61, 0x77, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x77, 0x66, - 0x69, 0x61, 0x74, 0x66, 0x75, 0x6e, 0x64, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x82, 0x01, 0x0a, 0x13, - 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, - 0x79, 0x49, 0x44, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, - 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, - 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, - 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, - 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x17, - 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x62, 0x79, 0x69, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x91, 0x01, 0x0a, 0x16, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, - 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, - 0x79, 0x44, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, - 0x22, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x77, 0x69, 0x74, 0x68, 0x64, 0x72, 0x61, 0x77, 0x61, 0x6c, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x79, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x73, - 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, - 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, - 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x73, 0x12, 0x76, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x67, 0x67, 0x65, - 0x72, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x3a, 0x01, 0x2a, 0x12, 0x76, 0x0a, 0x10, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x12, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x73, - 0x3a, 0x01, 0x2a, 0x12, 0x79, 0x0a, 0x12, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1b, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x65, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x7b, - 0x0a, 0x13, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x1b, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, - 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, - 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x65, 0x78, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x70, 0x61, 0x69, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, - 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x29, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, - 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6f, - 0x72, 0x64, 0x65, 0x72, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, - 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, - 0x6b, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x74, 0x69, 0x63, 0x6b, - 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, - 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x26, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x65, - 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, - 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x74, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0x67, 0x0a, - 0x0d, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, - 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, - 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x75, 0x64, 0x69, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x61, 0x75, 0x64, 0x69, - 0x74, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x74, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x74, 0x0a, 0x0f, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x1f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, - 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, - 0x01, 0x2a, 0x12, 0x78, 0x0a, 0x13, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, - 0x65, 0x61, 0x64, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x61, 0x64, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x70, 0x0a, 0x0f, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, - 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x6c, - 0x0a, 0x0e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x12, 0x1d, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x6e, 0x0a, 0x0d, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x1c, 0x2e, - 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, - 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x74, 0x0a, 0x10, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, - 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x3a, - 0x01, 0x2a, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, - 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x47, 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, - 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, - 0x73, 0x74, 0x6f, 0x70, 0x3a, 0x01, 0x2a, 0x12, 0x80, 0x01, 0x0a, 0x17, 0x47, 0x43, 0x54, 0x53, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x54, 0x6f, 0x67, - 0x67, 0x6c, 0x65, 0x12, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x43, 0x54, - 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x43, 0x54, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x22, - 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x63, 0x74, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2f, 0x61, - 0x75, 0x74, 0x6f, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x7b, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, - 0x12, 0x21, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, - 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, 0x43, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, - 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x74, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x69, 0x63, - 0x63, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +type WebsocketSetProxyRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Proxy string `protobuf:"bytes,2,opt,name=proxy,proto3" json:"proxy,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -var ( - file_rpc_proto_rawDescOnce sync.Once - file_rpc_proto_rawDescData = file_rpc_proto_rawDesc -) - -func file_rpc_proto_rawDescGZIP() []byte { - file_rpc_proto_rawDescOnce.Do(func() { - file_rpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_rpc_proto_rawDescData) - }) - return file_rpc_proto_rawDescData +func (m *WebsocketSetProxyRequest) Reset() { *m = WebsocketSetProxyRequest{} } +func (m *WebsocketSetProxyRequest) String() string { return proto.CompactTextString(m) } +func (*WebsocketSetProxyRequest) ProtoMessage() {} +func (*WebsocketSetProxyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{132} } -var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 141) -var file_rpc_proto_goTypes = []interface{}{ - (*GetInfoRequest)(nil), // 0: gctrpc.GetInfoRequest - (*GetInfoResponse)(nil), // 1: gctrpc.GetInfoResponse - (*GetCommunicationRelayersRequest)(nil), // 2: gctrpc.GetCommunicationRelayersRequest - (*CommunicationRelayer)(nil), // 3: gctrpc.CommunicationRelayer - (*GetCommunicationRelayersResponse)(nil), // 4: gctrpc.GetCommunicationRelayersResponse - (*GenericSubsystemRequest)(nil), // 5: gctrpc.GenericSubsystemRequest - (*GenericSubsystemResponse)(nil), // 6: gctrpc.GenericSubsystemResponse - (*GetSubsystemsRequest)(nil), // 7: gctrpc.GetSubsystemsRequest - (*GetSusbsytemsResponse)(nil), // 8: gctrpc.GetSusbsytemsResponse - (*GetRPCEndpointsRequest)(nil), // 9: gctrpc.GetRPCEndpointsRequest - (*RPCEndpoint)(nil), // 10: gctrpc.RPCEndpoint - (*GetRPCEndpointsResponse)(nil), // 11: gctrpc.GetRPCEndpointsResponse - (*GenericExchangeNameRequest)(nil), // 12: gctrpc.GenericExchangeNameRequest - (*GenericExchangeNameResponse)(nil), // 13: gctrpc.GenericExchangeNameResponse - (*GetExchangesRequest)(nil), // 14: gctrpc.GetExchangesRequest - (*GetExchangesResponse)(nil), // 15: gctrpc.GetExchangesResponse - (*GetExchangeOTPReponse)(nil), // 16: gctrpc.GetExchangeOTPReponse - (*GetExchangeOTPsRequest)(nil), // 17: gctrpc.GetExchangeOTPsRequest - (*GetExchangeOTPsResponse)(nil), // 18: gctrpc.GetExchangeOTPsResponse - (*DisableExchangeRequest)(nil), // 19: gctrpc.DisableExchangeRequest - (*PairsSupported)(nil), // 20: gctrpc.PairsSupported - (*GetExchangeInfoResponse)(nil), // 21: gctrpc.GetExchangeInfoResponse - (*GetTickerRequest)(nil), // 22: gctrpc.GetTickerRequest - (*CurrencyPair)(nil), // 23: gctrpc.CurrencyPair - (*TickerResponse)(nil), // 24: gctrpc.TickerResponse - (*GetTickersRequest)(nil), // 25: gctrpc.GetTickersRequest - (*Tickers)(nil), // 26: gctrpc.Tickers - (*GetTickersResponse)(nil), // 27: gctrpc.GetTickersResponse - (*GetOrderbookRequest)(nil), // 28: gctrpc.GetOrderbookRequest - (*OrderbookItem)(nil), // 29: gctrpc.OrderbookItem - (*OrderbookResponse)(nil), // 30: gctrpc.OrderbookResponse - (*GetOrderbooksRequest)(nil), // 31: gctrpc.GetOrderbooksRequest - (*Orderbooks)(nil), // 32: gctrpc.Orderbooks - (*GetOrderbooksResponse)(nil), // 33: gctrpc.GetOrderbooksResponse - (*GetAccountInfoRequest)(nil), // 34: gctrpc.GetAccountInfoRequest - (*Account)(nil), // 35: gctrpc.Account - (*AccountCurrencyInfo)(nil), // 36: gctrpc.AccountCurrencyInfo - (*GetAccountInfoResponse)(nil), // 37: gctrpc.GetAccountInfoResponse - (*GetConfigRequest)(nil), // 38: gctrpc.GetConfigRequest - (*GetConfigResponse)(nil), // 39: gctrpc.GetConfigResponse - (*PortfolioAddress)(nil), // 40: gctrpc.PortfolioAddress - (*GetPortfolioRequest)(nil), // 41: gctrpc.GetPortfolioRequest - (*GetPortfolioResponse)(nil), // 42: gctrpc.GetPortfolioResponse - (*GetPortfolioSummaryRequest)(nil), // 43: gctrpc.GetPortfolioSummaryRequest - (*Coin)(nil), // 44: gctrpc.Coin - (*OfflineCoinSummary)(nil), // 45: gctrpc.OfflineCoinSummary - (*OnlineCoinSummary)(nil), // 46: gctrpc.OnlineCoinSummary - (*OfflineCoins)(nil), // 47: gctrpc.OfflineCoins - (*OnlineCoins)(nil), // 48: gctrpc.OnlineCoins - (*GetPortfolioSummaryResponse)(nil), // 49: gctrpc.GetPortfolioSummaryResponse - (*AddPortfolioAddressRequest)(nil), // 50: gctrpc.AddPortfolioAddressRequest - (*AddPortfolioAddressResponse)(nil), // 51: gctrpc.AddPortfolioAddressResponse - (*RemovePortfolioAddressRequest)(nil), // 52: gctrpc.RemovePortfolioAddressRequest - (*RemovePortfolioAddressResponse)(nil), // 53: gctrpc.RemovePortfolioAddressResponse - (*GetForexProvidersRequest)(nil), // 54: gctrpc.GetForexProvidersRequest - (*ForexProvider)(nil), // 55: gctrpc.ForexProvider - (*GetForexProvidersResponse)(nil), // 56: gctrpc.GetForexProvidersResponse - (*GetForexRatesRequest)(nil), // 57: gctrpc.GetForexRatesRequest - (*ForexRatesConversion)(nil), // 58: gctrpc.ForexRatesConversion - (*GetForexRatesResponse)(nil), // 59: gctrpc.GetForexRatesResponse - (*OrderDetails)(nil), // 60: gctrpc.OrderDetails - (*TradeHistory)(nil), // 61: gctrpc.TradeHistory - (*GetOrdersRequest)(nil), // 62: gctrpc.GetOrdersRequest - (*GetOrdersResponse)(nil), // 63: gctrpc.GetOrdersResponse - (*GetOrderRequest)(nil), // 64: gctrpc.GetOrderRequest - (*SubmitOrderRequest)(nil), // 65: gctrpc.SubmitOrderRequest - (*SubmitOrderResponse)(nil), // 66: gctrpc.SubmitOrderResponse - (*SimulateOrderRequest)(nil), // 67: gctrpc.SimulateOrderRequest - (*SimulateOrderResponse)(nil), // 68: gctrpc.SimulateOrderResponse - (*WhaleBombRequest)(nil), // 69: gctrpc.WhaleBombRequest - (*CancelOrderRequest)(nil), // 70: gctrpc.CancelOrderRequest - (*CancelOrderResponse)(nil), // 71: gctrpc.CancelOrderResponse - (*CancelAllOrdersRequest)(nil), // 72: gctrpc.CancelAllOrdersRequest - (*CancelAllOrdersResponse)(nil), // 73: gctrpc.CancelAllOrdersResponse - (*GetEventsRequest)(nil), // 74: gctrpc.GetEventsRequest - (*ConditionParams)(nil), // 75: gctrpc.ConditionParams - (*GetEventsResponse)(nil), // 76: gctrpc.GetEventsResponse - (*AddEventRequest)(nil), // 77: gctrpc.AddEventRequest - (*AddEventResponse)(nil), // 78: gctrpc.AddEventResponse - (*RemoveEventRequest)(nil), // 79: gctrpc.RemoveEventRequest - (*RemoveEventResponse)(nil), // 80: gctrpc.RemoveEventResponse - (*GetCryptocurrencyDepositAddressesRequest)(nil), // 81: gctrpc.GetCryptocurrencyDepositAddressesRequest - (*GetCryptocurrencyDepositAddressesResponse)(nil), // 82: gctrpc.GetCryptocurrencyDepositAddressesResponse - (*GetCryptocurrencyDepositAddressRequest)(nil), // 83: gctrpc.GetCryptocurrencyDepositAddressRequest - (*GetCryptocurrencyDepositAddressResponse)(nil), // 84: gctrpc.GetCryptocurrencyDepositAddressResponse - (*WithdrawFiatRequest)(nil), // 85: gctrpc.WithdrawFiatRequest - (*WithdrawCryptoRequest)(nil), // 86: gctrpc.WithdrawCryptoRequest - (*WithdrawResponse)(nil), // 87: gctrpc.WithdrawResponse - (*WithdrawalEventByIDRequest)(nil), // 88: gctrpc.WithdrawalEventByIDRequest - (*WithdrawalEventByIDResponse)(nil), // 89: gctrpc.WithdrawalEventByIDResponse - (*WithdrawalEventsByExchangeRequest)(nil), // 90: gctrpc.WithdrawalEventsByExchangeRequest - (*WithdrawalEventsByDateRequest)(nil), // 91: gctrpc.WithdrawalEventsByDateRequest - (*WithdrawalEventsByExchangeResponse)(nil), // 92: gctrpc.WithdrawalEventsByExchangeResponse - (*WithdrawalEventResponse)(nil), // 93: gctrpc.WithdrawalEventResponse - (*WithdrawlExchangeEvent)(nil), // 94: gctrpc.WithdrawlExchangeEvent - (*WithdrawalRequestEvent)(nil), // 95: gctrpc.WithdrawalRequestEvent - (*FiatWithdrawalEvent)(nil), // 96: gctrpc.FiatWithdrawalEvent - (*CryptoWithdrawalEvent)(nil), // 97: gctrpc.CryptoWithdrawalEvent - (*GetLoggerDetailsRequest)(nil), // 98: gctrpc.GetLoggerDetailsRequest - (*GetLoggerDetailsResponse)(nil), // 99: gctrpc.GetLoggerDetailsResponse - (*SetLoggerDetailsRequest)(nil), // 100: gctrpc.SetLoggerDetailsRequest - (*GetExchangePairsRequest)(nil), // 101: gctrpc.GetExchangePairsRequest - (*GetExchangePairsResponse)(nil), // 102: gctrpc.GetExchangePairsResponse - (*ExchangePairRequest)(nil), // 103: gctrpc.ExchangePairRequest - (*GetOrderbookStreamRequest)(nil), // 104: gctrpc.GetOrderbookStreamRequest - (*GetExchangeOrderbookStreamRequest)(nil), // 105: gctrpc.GetExchangeOrderbookStreamRequest - (*GetTickerStreamRequest)(nil), // 106: gctrpc.GetTickerStreamRequest - (*GetExchangeTickerStreamRequest)(nil), // 107: gctrpc.GetExchangeTickerStreamRequest - (*GetAuditEventRequest)(nil), // 108: gctrpc.GetAuditEventRequest - (*GetAuditEventResponse)(nil), // 109: gctrpc.GetAuditEventResponse - (*GetHistoricCandlesRequest)(nil), // 110: gctrpc.GetHistoricCandlesRequest - (*GetHistoricCandlesResponse)(nil), // 111: gctrpc.GetHistoricCandlesResponse - (*Candle)(nil), // 112: gctrpc.Candle - (*AuditEvent)(nil), // 113: gctrpc.AuditEvent - (*GCTScript)(nil), // 114: gctrpc.GCTScript - (*GCTScriptExecuteRequest)(nil), // 115: gctrpc.GCTScriptExecuteRequest - (*GCTScriptStopRequest)(nil), // 116: gctrpc.GCTScriptStopRequest - (*GCTScriptStopAllRequest)(nil), // 117: gctrpc.GCTScriptStopAllRequest - (*GCTScriptStatusRequest)(nil), // 118: gctrpc.GCTScriptStatusRequest - (*GCTScriptListAllRequest)(nil), // 119: gctrpc.GCTScriptListAllRequest - (*GCTScriptUploadRequest)(nil), // 120: gctrpc.GCTScriptUploadRequest - (*GCTScriptReadScriptRequest)(nil), // 121: gctrpc.GCTScriptReadScriptRequest - (*GCTScriptQueryRequest)(nil), // 122: gctrpc.GCTScriptQueryRequest - (*GCTScriptAutoLoadRequest)(nil), // 123: gctrpc.GCTScriptAutoLoadRequest - (*GCTScriptStatusResponse)(nil), // 124: gctrpc.GCTScriptStatusResponse - (*GCTScriptQueryResponse)(nil), // 125: gctrpc.GCTScriptQueryResponse - (*GCTScriptGenericResponse)(nil), // 126: gctrpc.GCTScriptGenericResponse - nil, // 127: gctrpc.GetInfoResponse.SubsystemStatusEntry - nil, // 128: gctrpc.GetInfoResponse.RpcEndpointsEntry - nil, // 129: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - nil, // 130: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - nil, // 131: gctrpc.GetRPCEndpointsResponse.EndpointsEntry - nil, // 132: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - nil, // 133: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry - nil, // 134: gctrpc.OnlineCoins.CoinsEntry - nil, // 135: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry - nil, // 136: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry - (*CancelAllOrdersResponse_Orders)(nil), // 137: gctrpc.CancelAllOrdersResponse.Orders - nil, // 138: gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - nil, // 139: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry - nil, // 140: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry - (*timestamp.Timestamp)(nil), // 141: google.protobuf.Timestamp +func (m *WebsocketSetProxyRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WebsocketSetProxyRequest.Unmarshal(m, b) } -var file_rpc_proto_depIdxs = []int32{ - 127, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry - 128, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry - 129, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - 130, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - 131, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry - 132, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - 133, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry - 23, // 7: gctrpc.GetTickerRequest.pair:type_name -> gctrpc.CurrencyPair - 23, // 8: gctrpc.TickerResponse.pair:type_name -> gctrpc.CurrencyPair - 24, // 9: gctrpc.Tickers.tickers:type_name -> gctrpc.TickerResponse - 26, // 10: gctrpc.GetTickersResponse.tickers:type_name -> gctrpc.Tickers - 23, // 11: gctrpc.GetOrderbookRequest.pair:type_name -> gctrpc.CurrencyPair - 23, // 12: gctrpc.OrderbookResponse.pair:type_name -> gctrpc.CurrencyPair - 29, // 13: gctrpc.OrderbookResponse.bids:type_name -> gctrpc.OrderbookItem - 29, // 14: gctrpc.OrderbookResponse.asks:type_name -> gctrpc.OrderbookItem - 30, // 15: gctrpc.Orderbooks.orderbooks:type_name -> gctrpc.OrderbookResponse - 32, // 16: gctrpc.GetOrderbooksResponse.orderbooks:type_name -> gctrpc.Orderbooks - 36, // 17: gctrpc.Account.currencies:type_name -> gctrpc.AccountCurrencyInfo - 35, // 18: gctrpc.GetAccountInfoResponse.accounts:type_name -> gctrpc.Account - 40, // 19: gctrpc.GetPortfolioResponse.portfolio:type_name -> gctrpc.PortfolioAddress - 45, // 20: gctrpc.OfflineCoins.addresses:type_name -> gctrpc.OfflineCoinSummary - 134, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry - 44, // 22: gctrpc.GetPortfolioSummaryResponse.coin_totals:type_name -> gctrpc.Coin - 44, // 23: gctrpc.GetPortfolioSummaryResponse.coins_offline:type_name -> gctrpc.Coin - 135, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry - 44, // 25: gctrpc.GetPortfolioSummaryResponse.coins_online:type_name -> gctrpc.Coin - 136, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry - 55, // 27: gctrpc.GetForexProvidersResponse.forex_providers:type_name -> gctrpc.ForexProvider - 58, // 28: gctrpc.GetForexRatesResponse.forex_rates:type_name -> gctrpc.ForexRatesConversion - 61, // 29: gctrpc.OrderDetails.trades:type_name -> gctrpc.TradeHistory - 23, // 30: gctrpc.GetOrdersRequest.pair:type_name -> gctrpc.CurrencyPair - 60, // 31: gctrpc.GetOrdersResponse.orders:type_name -> gctrpc.OrderDetails - 23, // 32: gctrpc.SubmitOrderRequest.pair:type_name -> gctrpc.CurrencyPair - 23, // 33: gctrpc.SimulateOrderRequest.pair:type_name -> gctrpc.CurrencyPair - 29, // 34: gctrpc.SimulateOrderResponse.orders:type_name -> gctrpc.OrderbookItem - 23, // 35: gctrpc.WhaleBombRequest.pair:type_name -> gctrpc.CurrencyPair - 23, // 36: gctrpc.CancelOrderRequest.pair:type_name -> gctrpc.CurrencyPair - 137, // 37: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.CancelAllOrdersResponse.Orders - 75, // 38: gctrpc.GetEventsResponse.condition_params:type_name -> gctrpc.ConditionParams - 23, // 39: gctrpc.GetEventsResponse.pair:type_name -> gctrpc.CurrencyPair - 75, // 40: gctrpc.AddEventRequest.condition_params:type_name -> gctrpc.ConditionParams - 23, // 41: gctrpc.AddEventRequest.pair:type_name -> gctrpc.CurrencyPair - 139, // 42: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry - 93, // 43: gctrpc.WithdrawalEventByIDResponse.event:type_name -> gctrpc.WithdrawalEventResponse - 93, // 44: gctrpc.WithdrawalEventsByExchangeResponse.event:type_name -> gctrpc.WithdrawalEventResponse - 94, // 45: gctrpc.WithdrawalEventResponse.exchange:type_name -> gctrpc.WithdrawlExchangeEvent - 95, // 46: gctrpc.WithdrawalEventResponse.request:type_name -> gctrpc.WithdrawalRequestEvent - 141, // 47: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp - 141, // 48: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp - 96, // 49: gctrpc.WithdrawalRequestEvent.fiat:type_name -> gctrpc.FiatWithdrawalEvent - 97, // 50: gctrpc.WithdrawalRequestEvent.crypto:type_name -> gctrpc.CryptoWithdrawalEvent - 140, // 51: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry - 23, // 52: gctrpc.ExchangePairRequest.pair:type_name -> gctrpc.CurrencyPair - 23, // 53: gctrpc.GetOrderbookStreamRequest.pair:type_name -> gctrpc.CurrencyPair - 23, // 54: gctrpc.GetTickerStreamRequest.pair:type_name -> gctrpc.CurrencyPair - 113, // 55: gctrpc.GetAuditEventResponse.events:type_name -> gctrpc.AuditEvent - 23, // 56: gctrpc.GetHistoricCandlesRequest.pair:type_name -> gctrpc.CurrencyPair - 23, // 57: gctrpc.GetHistoricCandlesResponse.pair:type_name -> gctrpc.CurrencyPair - 112, // 58: gctrpc.GetHistoricCandlesResponse.candle:type_name -> gctrpc.Candle - 114, // 59: gctrpc.GCTScriptExecuteRequest.script:type_name -> gctrpc.GCTScript - 114, // 60: gctrpc.GCTScriptStopRequest.script:type_name -> gctrpc.GCTScript - 114, // 61: gctrpc.GCTScriptReadScriptRequest.script:type_name -> gctrpc.GCTScript - 114, // 62: gctrpc.GCTScriptQueryRequest.script:type_name -> gctrpc.GCTScript - 114, // 63: gctrpc.GCTScriptStatusResponse.scripts:type_name -> gctrpc.GCTScript - 114, // 64: gctrpc.GCTScriptQueryResponse.script:type_name -> gctrpc.GCTScript - 10, // 65: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 3, // 66: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer - 10, // 67: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 20, // 68: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 46, // 69: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary - 47, // 70: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins - 48, // 71: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins - 138, // 72: gctrpc.CancelAllOrdersResponse.Orders.order_status:type_name -> gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry - 20, // 73: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 0, // 74: gctrpc.GoCryptoTrader.GetInfo:input_type -> gctrpc.GetInfoRequest - 7, // 75: gctrpc.GoCryptoTrader.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest - 5, // 76: gctrpc.GoCryptoTrader.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 5, // 77: gctrpc.GoCryptoTrader.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 9, // 78: gctrpc.GoCryptoTrader.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest - 2, // 79: gctrpc.GoCryptoTrader.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest - 14, // 80: gctrpc.GoCryptoTrader.GetExchanges:input_type -> gctrpc.GetExchangesRequest - 12, // 81: gctrpc.GoCryptoTrader.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 12, // 82: gctrpc.GoCryptoTrader.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest - 12, // 83: gctrpc.GoCryptoTrader.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest - 17, // 84: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest - 12, // 85: gctrpc.GoCryptoTrader.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 22, // 86: gctrpc.GoCryptoTrader.GetTicker:input_type -> gctrpc.GetTickerRequest - 25, // 87: gctrpc.GoCryptoTrader.GetTickers:input_type -> gctrpc.GetTickersRequest - 28, // 88: gctrpc.GoCryptoTrader.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest - 31, // 89: gctrpc.GoCryptoTrader.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest - 34, // 90: gctrpc.GoCryptoTrader.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 34, // 91: gctrpc.GoCryptoTrader.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest - 38, // 92: gctrpc.GoCryptoTrader.GetConfig:input_type -> gctrpc.GetConfigRequest - 41, // 93: gctrpc.GoCryptoTrader.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest - 43, // 94: gctrpc.GoCryptoTrader.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest - 50, // 95: gctrpc.GoCryptoTrader.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest - 52, // 96: gctrpc.GoCryptoTrader.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest - 54, // 97: gctrpc.GoCryptoTrader.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest - 57, // 98: gctrpc.GoCryptoTrader.GetForexRates:input_type -> gctrpc.GetForexRatesRequest - 62, // 99: gctrpc.GoCryptoTrader.GetOrders:input_type -> gctrpc.GetOrdersRequest - 64, // 100: gctrpc.GoCryptoTrader.GetOrder:input_type -> gctrpc.GetOrderRequest - 65, // 101: gctrpc.GoCryptoTrader.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest - 67, // 102: gctrpc.GoCryptoTrader.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest - 69, // 103: gctrpc.GoCryptoTrader.WhaleBomb:input_type -> gctrpc.WhaleBombRequest - 70, // 104: gctrpc.GoCryptoTrader.CancelOrder:input_type -> gctrpc.CancelOrderRequest - 72, // 105: gctrpc.GoCryptoTrader.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest - 74, // 106: gctrpc.GoCryptoTrader.GetEvents:input_type -> gctrpc.GetEventsRequest - 77, // 107: gctrpc.GoCryptoTrader.AddEvent:input_type -> gctrpc.AddEventRequest - 79, // 108: gctrpc.GoCryptoTrader.RemoveEvent:input_type -> gctrpc.RemoveEventRequest - 81, // 109: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest - 83, // 110: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest - 85, // 111: gctrpc.GoCryptoTrader.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest - 86, // 112: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest - 88, // 113: gctrpc.GoCryptoTrader.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest - 90, // 114: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest - 91, // 115: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest - 98, // 116: gctrpc.GoCryptoTrader.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest - 100, // 117: gctrpc.GoCryptoTrader.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest - 101, // 118: gctrpc.GoCryptoTrader.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest - 103, // 119: gctrpc.GoCryptoTrader.EnableExchangePair:input_type -> gctrpc.ExchangePairRequest - 103, // 120: gctrpc.GoCryptoTrader.DisableExchangePair:input_type -> gctrpc.ExchangePairRequest - 104, // 121: gctrpc.GoCryptoTrader.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest - 105, // 122: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest - 106, // 123: gctrpc.GoCryptoTrader.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest - 107, // 124: gctrpc.GoCryptoTrader.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest - 108, // 125: gctrpc.GoCryptoTrader.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest - 115, // 126: gctrpc.GoCryptoTrader.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest - 120, // 127: gctrpc.GoCryptoTrader.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest - 121, // 128: gctrpc.GoCryptoTrader.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest - 118, // 129: gctrpc.GoCryptoTrader.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest - 122, // 130: gctrpc.GoCryptoTrader.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest - 116, // 131: gctrpc.GoCryptoTrader.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest - 117, // 132: gctrpc.GoCryptoTrader.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest - 119, // 133: gctrpc.GoCryptoTrader.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest - 123, // 134: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest - 110, // 135: gctrpc.GoCryptoTrader.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest - 1, // 136: gctrpc.GoCryptoTrader.GetInfo:output_type -> gctrpc.GetInfoResponse - 8, // 137: gctrpc.GoCryptoTrader.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse - 6, // 138: gctrpc.GoCryptoTrader.EnableSubsystem:output_type -> gctrpc.GenericSubsystemResponse - 6, // 139: gctrpc.GoCryptoTrader.DisableSubsystem:output_type -> gctrpc.GenericSubsystemResponse - 11, // 140: gctrpc.GoCryptoTrader.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse - 4, // 141: gctrpc.GoCryptoTrader.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse - 15, // 142: gctrpc.GoCryptoTrader.GetExchanges:output_type -> gctrpc.GetExchangesResponse - 13, // 143: gctrpc.GoCryptoTrader.DisableExchange:output_type -> gctrpc.GenericExchangeNameResponse - 21, // 144: gctrpc.GoCryptoTrader.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse - 16, // 145: gctrpc.GoCryptoTrader.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPReponse - 18, // 146: gctrpc.GoCryptoTrader.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse - 13, // 147: gctrpc.GoCryptoTrader.EnableExchange:output_type -> gctrpc.GenericExchangeNameResponse - 24, // 148: gctrpc.GoCryptoTrader.GetTicker:output_type -> gctrpc.TickerResponse - 27, // 149: gctrpc.GoCryptoTrader.GetTickers:output_type -> gctrpc.GetTickersResponse - 30, // 150: gctrpc.GoCryptoTrader.GetOrderbook:output_type -> gctrpc.OrderbookResponse - 33, // 151: gctrpc.GoCryptoTrader.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse - 37, // 152: gctrpc.GoCryptoTrader.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 37, // 153: gctrpc.GoCryptoTrader.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse - 39, // 154: gctrpc.GoCryptoTrader.GetConfig:output_type -> gctrpc.GetConfigResponse - 42, // 155: gctrpc.GoCryptoTrader.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse - 49, // 156: gctrpc.GoCryptoTrader.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse - 51, // 157: gctrpc.GoCryptoTrader.AddPortfolioAddress:output_type -> gctrpc.AddPortfolioAddressResponse - 53, // 158: gctrpc.GoCryptoTrader.RemovePortfolioAddress:output_type -> gctrpc.RemovePortfolioAddressResponse - 56, // 159: gctrpc.GoCryptoTrader.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse - 59, // 160: gctrpc.GoCryptoTrader.GetForexRates:output_type -> gctrpc.GetForexRatesResponse - 63, // 161: gctrpc.GoCryptoTrader.GetOrders:output_type -> gctrpc.GetOrdersResponse - 60, // 162: gctrpc.GoCryptoTrader.GetOrder:output_type -> gctrpc.OrderDetails - 66, // 163: gctrpc.GoCryptoTrader.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse - 68, // 164: gctrpc.GoCryptoTrader.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse - 68, // 165: gctrpc.GoCryptoTrader.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse - 71, // 166: gctrpc.GoCryptoTrader.CancelOrder:output_type -> gctrpc.CancelOrderResponse - 73, // 167: gctrpc.GoCryptoTrader.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse - 76, // 168: gctrpc.GoCryptoTrader.GetEvents:output_type -> gctrpc.GetEventsResponse - 78, // 169: gctrpc.GoCryptoTrader.AddEvent:output_type -> gctrpc.AddEventResponse - 80, // 170: gctrpc.GoCryptoTrader.RemoveEvent:output_type -> gctrpc.RemoveEventResponse - 82, // 171: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse - 84, // 172: gctrpc.GoCryptoTrader.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse - 87, // 173: gctrpc.GoCryptoTrader.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse - 87, // 174: gctrpc.GoCryptoTrader.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse - 89, // 175: gctrpc.GoCryptoTrader.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse - 92, // 176: gctrpc.GoCryptoTrader.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 92, // 177: gctrpc.GoCryptoTrader.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 99, // 178: gctrpc.GoCryptoTrader.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 99, // 179: gctrpc.GoCryptoTrader.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 102, // 180: gctrpc.GoCryptoTrader.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse - 13, // 181: gctrpc.GoCryptoTrader.EnableExchangePair:output_type -> gctrpc.GenericExchangeNameResponse - 13, // 182: gctrpc.GoCryptoTrader.DisableExchangePair:output_type -> gctrpc.GenericExchangeNameResponse - 30, // 183: gctrpc.GoCryptoTrader.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse - 30, // 184: gctrpc.GoCryptoTrader.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse - 24, // 185: gctrpc.GoCryptoTrader.GetTickerStream:output_type -> gctrpc.TickerResponse - 24, // 186: gctrpc.GoCryptoTrader.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse - 109, // 187: gctrpc.GoCryptoTrader.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse - 126, // 188: gctrpc.GoCryptoTrader.GCTScriptExecute:output_type -> gctrpc.GCTScriptGenericResponse - 126, // 189: gctrpc.GoCryptoTrader.GCTScriptUpload:output_type -> gctrpc.GCTScriptGenericResponse - 125, // 190: gctrpc.GoCryptoTrader.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse - 124, // 191: gctrpc.GoCryptoTrader.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse - 125, // 192: gctrpc.GoCryptoTrader.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse - 126, // 193: gctrpc.GoCryptoTrader.GCTScriptStop:output_type -> gctrpc.GCTScriptGenericResponse - 126, // 194: gctrpc.GoCryptoTrader.GCTScriptStopAll:output_type -> gctrpc.GCTScriptGenericResponse - 124, // 195: gctrpc.GoCryptoTrader.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse - 126, // 196: gctrpc.GoCryptoTrader.GCTScriptAutoLoadToggle:output_type -> gctrpc.GCTScriptGenericResponse - 111, // 197: gctrpc.GoCryptoTrader.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 136, // [136:198] is the sub-list for method output_type - 74, // [74:136] is the sub-list for method input_type - 74, // [74:74] is the sub-list for extension type_name - 74, // [74:74] is the sub-list for extension extendee - 0, // [0:74] is the sub-list for field type_name +func (m *WebsocketSetProxyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WebsocketSetProxyRequest.Marshal(b, m, deterministic) +} +func (m *WebsocketSetProxyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WebsocketSetProxyRequest.Merge(m, src) +} +func (m *WebsocketSetProxyRequest) XXX_Size() int { + return xxx_messageInfo_WebsocketSetProxyRequest.Size(m) +} +func (m *WebsocketSetProxyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WebsocketSetProxyRequest.DiscardUnknown(m) } -func init() { file_rpc_proto_init() } -func file_rpc_proto_init() { - if File_rpc_proto != nil { - return +var xxx_messageInfo_WebsocketSetProxyRequest proto.InternalMessageInfo + +func (m *WebsocketSetProxyRequest) GetExchange() string { + if m != nil { + return m.Exchange } - if !protoimpl.UnsafeEnabled { - file_rpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetInfoRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetInfoResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCommunicationRelayersRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommunicationRelayer); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCommunicationRelayersResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenericSubsystemRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenericSubsystemResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSubsystemsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSusbsytemsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRPCEndpointsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RPCEndpoint); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetRPCEndpointsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenericExchangeNameRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenericExchangeNameResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangeOTPReponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangeOTPsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangeOTPsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DisableExchangeRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PairsSupported); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangeInfoResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTickerRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CurrencyPair); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TickerResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTickersRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Tickers); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTickersResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetOrderbookRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OrderbookItem); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OrderbookResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetOrderbooksRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Orderbooks); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetOrderbooksResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAccountInfoRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Account); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AccountCurrencyInfo); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAccountInfoResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetConfigRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetConfigResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PortfolioAddress); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPortfolioRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPortfolioResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPortfolioSummaryRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Coin); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OfflineCoinSummary); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OnlineCoinSummary); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OfflineCoins); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OnlineCoins); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPortfolioSummaryResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddPortfolioAddressRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddPortfolioAddressResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemovePortfolioAddressRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemovePortfolioAddressResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetForexProvidersRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForexProvider); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetForexProvidersResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetForexRatesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForexRatesConversion); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetForexRatesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*OrderDetails); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TradeHistory); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetOrdersRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetOrdersResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetOrderRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubmitOrderRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubmitOrderResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SimulateOrderRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SimulateOrderResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WhaleBombRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelOrderRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelOrderResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelAllOrdersRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelAllOrdersResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetEventsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ConditionParams); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetEventsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddEventRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AddEventResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveEventRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RemoveEventResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCryptocurrencyDepositAddressesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCryptocurrencyDepositAddressesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCryptocurrencyDepositAddressRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetCryptocurrencyDepositAddressResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawFiatRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawCryptoRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawalEventByIDRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawalEventByIDResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawalEventsByExchangeRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawalEventsByDateRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawalEventsByExchangeResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawalEventResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawlExchangeEvent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WithdrawalRequestEvent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FiatWithdrawalEvent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CryptoWithdrawalEvent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetLoggerDetailsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetLoggerDetailsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SetLoggerDetailsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangePairsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangePairsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExchangePairRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetOrderbookStreamRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[105].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangeOrderbookStreamRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTickerStreamRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[107].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetExchangeTickerStreamRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAuditEventRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[109].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAuditEventResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetHistoricCandlesRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[111].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetHistoricCandlesResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[112].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Candle); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[113].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuditEvent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[114].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScript); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[115].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptExecuteRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[116].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptStopRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[117].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptStopAllRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[118].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptStatusRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[119].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptListAllRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[120].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptUploadRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[121].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptReadScriptRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[122].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptQueryRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[123].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptAutoLoadRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[124].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptStatusResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[125].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptQueryResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[126].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GCTScriptGenericResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_rpc_proto_msgTypes[137].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelAllOrdersResponse_Orders); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } + return "" +} + +func (m *WebsocketSetProxyRequest) GetProxy() string { + if m != nil { + return m.Proxy } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_rpc_proto_rawDesc, - NumEnums: 0, - NumMessages: 141, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_rpc_proto_goTypes, - DependencyIndexes: file_rpc_proto_depIdxs, - MessageInfos: file_rpc_proto_msgTypes, - }.Build() - File_rpc_proto = out.File - file_rpc_proto_rawDesc = nil - file_rpc_proto_goTypes = nil - file_rpc_proto_depIdxs = nil + return "" +} + +type WebsocketSetURLRequest struct { + Exchange string `protobuf:"bytes,1,opt,name=exchange,proto3" json:"exchange,omitempty"` + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WebsocketSetURLRequest) Reset() { *m = WebsocketSetURLRequest{} } +func (m *WebsocketSetURLRequest) String() string { return proto.CompactTextString(m) } +func (*WebsocketSetURLRequest) ProtoMessage() {} +func (*WebsocketSetURLRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_77a6da22d6a3feb1, []int{133} +} + +func (m *WebsocketSetURLRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WebsocketSetURLRequest.Unmarshal(m, b) +} +func (m *WebsocketSetURLRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WebsocketSetURLRequest.Marshal(b, m, deterministic) +} +func (m *WebsocketSetURLRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WebsocketSetURLRequest.Merge(m, src) +} +func (m *WebsocketSetURLRequest) XXX_Size() int { + return xxx_messageInfo_WebsocketSetURLRequest.Size(m) +} +func (m *WebsocketSetURLRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WebsocketSetURLRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WebsocketSetURLRequest proto.InternalMessageInfo + +func (m *WebsocketSetURLRequest) GetExchange() string { + if m != nil { + return m.Exchange + } + return "" +} + +func (m *WebsocketSetURLRequest) GetUrl() string { + if m != nil { + return m.Url + } + return "" +} + +func init() { + proto.RegisterType((*GetInfoRequest)(nil), "gctrpc.GetInfoRequest") + proto.RegisterType((*GetInfoResponse)(nil), "gctrpc.GetInfoResponse") + proto.RegisterMapType((map[string]*RPCEndpoint)(nil), "gctrpc.GetInfoResponse.RpcEndpointsEntry") + proto.RegisterMapType((map[string]bool)(nil), "gctrpc.GetInfoResponse.SubsystemStatusEntry") + proto.RegisterType((*GetCommunicationRelayersRequest)(nil), "gctrpc.GetCommunicationRelayersRequest") + proto.RegisterType((*CommunicationRelayer)(nil), "gctrpc.CommunicationRelayer") + proto.RegisterType((*GetCommunicationRelayersResponse)(nil), "gctrpc.GetCommunicationRelayersResponse") + proto.RegisterMapType((map[string]*CommunicationRelayer)(nil), "gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry") + proto.RegisterType((*GenericSubsystemRequest)(nil), "gctrpc.GenericSubsystemRequest") + proto.RegisterType((*GetSubsystemsRequest)(nil), "gctrpc.GetSubsystemsRequest") + proto.RegisterType((*GetSusbsytemsResponse)(nil), "gctrpc.GetSusbsytemsResponse") + proto.RegisterMapType((map[string]bool)(nil), "gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry") + proto.RegisterType((*GetRPCEndpointsRequest)(nil), "gctrpc.GetRPCEndpointsRequest") + proto.RegisterType((*RPCEndpoint)(nil), "gctrpc.RPCEndpoint") + proto.RegisterType((*GetRPCEndpointsResponse)(nil), "gctrpc.GetRPCEndpointsResponse") + proto.RegisterMapType((map[string]*RPCEndpoint)(nil), "gctrpc.GetRPCEndpointsResponse.EndpointsEntry") + proto.RegisterType((*GenericExchangeNameRequest)(nil), "gctrpc.GenericExchangeNameRequest") + proto.RegisterType((*GetExchangesRequest)(nil), "gctrpc.GetExchangesRequest") + proto.RegisterType((*GetExchangesResponse)(nil), "gctrpc.GetExchangesResponse") + proto.RegisterType((*GetExchangeOTPReponse)(nil), "gctrpc.GetExchangeOTPReponse") + proto.RegisterType((*GetExchangeOTPsRequest)(nil), "gctrpc.GetExchangeOTPsRequest") + proto.RegisterType((*GetExchangeOTPsResponse)(nil), "gctrpc.GetExchangeOTPsResponse") + proto.RegisterMapType((map[string]string)(nil), "gctrpc.GetExchangeOTPsResponse.OtpCodesEntry") + proto.RegisterType((*DisableExchangeRequest)(nil), "gctrpc.DisableExchangeRequest") + proto.RegisterType((*PairsSupported)(nil), "gctrpc.PairsSupported") + proto.RegisterType((*GetExchangeInfoResponse)(nil), "gctrpc.GetExchangeInfoResponse") + proto.RegisterMapType((map[string]*PairsSupported)(nil), "gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry") + proto.RegisterType((*GetTickerRequest)(nil), "gctrpc.GetTickerRequest") + proto.RegisterType((*CurrencyPair)(nil), "gctrpc.CurrencyPair") + proto.RegisterType((*TickerResponse)(nil), "gctrpc.TickerResponse") + proto.RegisterType((*GetTickersRequest)(nil), "gctrpc.GetTickersRequest") + proto.RegisterType((*Tickers)(nil), "gctrpc.Tickers") + proto.RegisterType((*GetTickersResponse)(nil), "gctrpc.GetTickersResponse") + proto.RegisterType((*GetOrderbookRequest)(nil), "gctrpc.GetOrderbookRequest") + proto.RegisterType((*OrderbookItem)(nil), "gctrpc.OrderbookItem") + proto.RegisterType((*OrderbookResponse)(nil), "gctrpc.OrderbookResponse") + proto.RegisterType((*GetOrderbooksRequest)(nil), "gctrpc.GetOrderbooksRequest") + proto.RegisterType((*Orderbooks)(nil), "gctrpc.Orderbooks") + proto.RegisterType((*GetOrderbooksResponse)(nil), "gctrpc.GetOrderbooksResponse") + proto.RegisterType((*GetAccountInfoRequest)(nil), "gctrpc.GetAccountInfoRequest") + proto.RegisterType((*Account)(nil), "gctrpc.Account") + proto.RegisterType((*AccountCurrencyInfo)(nil), "gctrpc.AccountCurrencyInfo") + proto.RegisterType((*GetAccountInfoResponse)(nil), "gctrpc.GetAccountInfoResponse") + proto.RegisterType((*GetConfigRequest)(nil), "gctrpc.GetConfigRequest") + proto.RegisterType((*GetConfigResponse)(nil), "gctrpc.GetConfigResponse") + proto.RegisterType((*PortfolioAddress)(nil), "gctrpc.PortfolioAddress") + proto.RegisterType((*GetPortfolioRequest)(nil), "gctrpc.GetPortfolioRequest") + proto.RegisterType((*GetPortfolioResponse)(nil), "gctrpc.GetPortfolioResponse") + proto.RegisterType((*GetPortfolioSummaryRequest)(nil), "gctrpc.GetPortfolioSummaryRequest") + proto.RegisterType((*Coin)(nil), "gctrpc.Coin") + proto.RegisterType((*OfflineCoinSummary)(nil), "gctrpc.OfflineCoinSummary") + proto.RegisterType((*OnlineCoinSummary)(nil), "gctrpc.OnlineCoinSummary") + proto.RegisterType((*OfflineCoins)(nil), "gctrpc.OfflineCoins") + proto.RegisterType((*OnlineCoins)(nil), "gctrpc.OnlineCoins") + proto.RegisterMapType((map[string]*OnlineCoinSummary)(nil), "gctrpc.OnlineCoins.CoinsEntry") + proto.RegisterType((*GetPortfolioSummaryResponse)(nil), "gctrpc.GetPortfolioSummaryResponse") + proto.RegisterMapType((map[string]*OfflineCoins)(nil), "gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry") + proto.RegisterMapType((map[string]*OnlineCoins)(nil), "gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry") + proto.RegisterType((*AddPortfolioAddressRequest)(nil), "gctrpc.AddPortfolioAddressRequest") + proto.RegisterType((*RemovePortfolioAddressRequest)(nil), "gctrpc.RemovePortfolioAddressRequest") + proto.RegisterType((*GetForexProvidersRequest)(nil), "gctrpc.GetForexProvidersRequest") + proto.RegisterType((*ForexProvider)(nil), "gctrpc.ForexProvider") + proto.RegisterType((*GetForexProvidersResponse)(nil), "gctrpc.GetForexProvidersResponse") + proto.RegisterType((*GetForexRatesRequest)(nil), "gctrpc.GetForexRatesRequest") + proto.RegisterType((*ForexRatesConversion)(nil), "gctrpc.ForexRatesConversion") + proto.RegisterType((*GetForexRatesResponse)(nil), "gctrpc.GetForexRatesResponse") + proto.RegisterType((*OrderDetails)(nil), "gctrpc.OrderDetails") + proto.RegisterType((*TradeHistory)(nil), "gctrpc.TradeHistory") + proto.RegisterType((*GetOrdersRequest)(nil), "gctrpc.GetOrdersRequest") + proto.RegisterType((*GetOrdersResponse)(nil), "gctrpc.GetOrdersResponse") + proto.RegisterType((*GetOrderRequest)(nil), "gctrpc.GetOrderRequest") + proto.RegisterType((*SubmitOrderRequest)(nil), "gctrpc.SubmitOrderRequest") + proto.RegisterType((*SubmitOrderResponse)(nil), "gctrpc.SubmitOrderResponse") + proto.RegisterType((*SimulateOrderRequest)(nil), "gctrpc.SimulateOrderRequest") + proto.RegisterType((*SimulateOrderResponse)(nil), "gctrpc.SimulateOrderResponse") + proto.RegisterType((*WhaleBombRequest)(nil), "gctrpc.WhaleBombRequest") + proto.RegisterType((*CancelOrderRequest)(nil), "gctrpc.CancelOrderRequest") + proto.RegisterType((*CancelAllOrdersRequest)(nil), "gctrpc.CancelAllOrdersRequest") + proto.RegisterType((*CancelAllOrdersResponse)(nil), "gctrpc.CancelAllOrdersResponse") + proto.RegisterType((*CancelAllOrdersResponse_Orders)(nil), "gctrpc.CancelAllOrdersResponse.Orders") + proto.RegisterMapType((map[string]string)(nil), "gctrpc.CancelAllOrdersResponse.Orders.OrderStatusEntry") + proto.RegisterType((*GetEventsRequest)(nil), "gctrpc.GetEventsRequest") + proto.RegisterType((*ConditionParams)(nil), "gctrpc.ConditionParams") + proto.RegisterType((*GetEventsResponse)(nil), "gctrpc.GetEventsResponse") + proto.RegisterType((*AddEventRequest)(nil), "gctrpc.AddEventRequest") + proto.RegisterType((*AddEventResponse)(nil), "gctrpc.AddEventResponse") + proto.RegisterType((*RemoveEventRequest)(nil), "gctrpc.RemoveEventRequest") + proto.RegisterType((*GetCryptocurrencyDepositAddressesRequest)(nil), "gctrpc.GetCryptocurrencyDepositAddressesRequest") + proto.RegisterType((*GetCryptocurrencyDepositAddressesResponse)(nil), "gctrpc.GetCryptocurrencyDepositAddressesResponse") + proto.RegisterMapType((map[string]string)(nil), "gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry") + proto.RegisterType((*GetCryptocurrencyDepositAddressRequest)(nil), "gctrpc.GetCryptocurrencyDepositAddressRequest") + proto.RegisterType((*GetCryptocurrencyDepositAddressResponse)(nil), "gctrpc.GetCryptocurrencyDepositAddressResponse") + proto.RegisterType((*WithdrawFiatRequest)(nil), "gctrpc.WithdrawFiatRequest") + proto.RegisterType((*WithdrawCryptoRequest)(nil), "gctrpc.WithdrawCryptoRequest") + proto.RegisterType((*WithdrawResponse)(nil), "gctrpc.WithdrawResponse") + proto.RegisterType((*WithdrawalEventByIDRequest)(nil), "gctrpc.WithdrawalEventByIDRequest") + proto.RegisterType((*WithdrawalEventByIDResponse)(nil), "gctrpc.WithdrawalEventByIDResponse") + proto.RegisterType((*WithdrawalEventsByExchangeRequest)(nil), "gctrpc.WithdrawalEventsByExchangeRequest") + proto.RegisterType((*WithdrawalEventsByDateRequest)(nil), "gctrpc.WithdrawalEventsByDateRequest") + proto.RegisterType((*WithdrawalEventsByExchangeResponse)(nil), "gctrpc.WithdrawalEventsByExchangeResponse") + proto.RegisterType((*WithdrawalEventResponse)(nil), "gctrpc.WithdrawalEventResponse") + proto.RegisterType((*WithdrawlExchangeEvent)(nil), "gctrpc.WithdrawlExchangeEvent") + proto.RegisterType((*WithdrawalRequestEvent)(nil), "gctrpc.WithdrawalRequestEvent") + proto.RegisterType((*FiatWithdrawalEvent)(nil), "gctrpc.FiatWithdrawalEvent") + proto.RegisterType((*CryptoWithdrawalEvent)(nil), "gctrpc.CryptoWithdrawalEvent") + proto.RegisterType((*GetLoggerDetailsRequest)(nil), "gctrpc.GetLoggerDetailsRequest") + proto.RegisterType((*GetLoggerDetailsResponse)(nil), "gctrpc.GetLoggerDetailsResponse") + proto.RegisterType((*SetLoggerDetailsRequest)(nil), "gctrpc.SetLoggerDetailsRequest") + proto.RegisterType((*GetExchangePairsRequest)(nil), "gctrpc.GetExchangePairsRequest") + proto.RegisterType((*GetExchangePairsResponse)(nil), "gctrpc.GetExchangePairsResponse") + proto.RegisterMapType((map[string]*PairsSupported)(nil), "gctrpc.GetExchangePairsResponse.SupportedAssetsEntry") + proto.RegisterType((*SetExchangePairRequest)(nil), "gctrpc.SetExchangePairRequest") + proto.RegisterType((*GetOrderbookStreamRequest)(nil), "gctrpc.GetOrderbookStreamRequest") + proto.RegisterType((*GetExchangeOrderbookStreamRequest)(nil), "gctrpc.GetExchangeOrderbookStreamRequest") + proto.RegisterType((*GetTickerStreamRequest)(nil), "gctrpc.GetTickerStreamRequest") + proto.RegisterType((*GetExchangeTickerStreamRequest)(nil), "gctrpc.GetExchangeTickerStreamRequest") + proto.RegisterType((*GetAuditEventRequest)(nil), "gctrpc.GetAuditEventRequest") + proto.RegisterType((*GetAuditEventResponse)(nil), "gctrpc.GetAuditEventResponse") + proto.RegisterType((*GetHistoricCandlesRequest)(nil), "gctrpc.GetHistoricCandlesRequest") + proto.RegisterType((*GetHistoricCandlesResponse)(nil), "gctrpc.GetHistoricCandlesResponse") + proto.RegisterType((*Candle)(nil), "gctrpc.Candle") + proto.RegisterType((*AuditEvent)(nil), "gctrpc.AuditEvent") + proto.RegisterType((*GCTScript)(nil), "gctrpc.GCTScript") + proto.RegisterType((*GCTScriptExecuteRequest)(nil), "gctrpc.GCTScriptExecuteRequest") + proto.RegisterType((*GCTScriptStopRequest)(nil), "gctrpc.GCTScriptStopRequest") + proto.RegisterType((*GCTScriptStopAllRequest)(nil), "gctrpc.GCTScriptStopAllRequest") + proto.RegisterType((*GCTScriptStatusRequest)(nil), "gctrpc.GCTScriptStatusRequest") + proto.RegisterType((*GCTScriptListAllRequest)(nil), "gctrpc.GCTScriptListAllRequest") + proto.RegisterType((*GCTScriptUploadRequest)(nil), "gctrpc.GCTScriptUploadRequest") + proto.RegisterType((*GCTScriptReadScriptRequest)(nil), "gctrpc.GCTScriptReadScriptRequest") + proto.RegisterType((*GCTScriptQueryRequest)(nil), "gctrpc.GCTScriptQueryRequest") + proto.RegisterType((*GCTScriptAutoLoadRequest)(nil), "gctrpc.GCTScriptAutoLoadRequest") + proto.RegisterType((*GCTScriptStatusResponse)(nil), "gctrpc.GCTScriptStatusResponse") + proto.RegisterType((*GCTScriptQueryResponse)(nil), "gctrpc.GCTScriptQueryResponse") + proto.RegisterType((*GenericResponse)(nil), "gctrpc.GenericResponse") + proto.RegisterType((*SetExchangeAssetRequest)(nil), "gctrpc.SetExchangeAssetRequest") + proto.RegisterType((*SetExchangeAllPairsRequest)(nil), "gctrpc.SetExchangeAllPairsRequest") + proto.RegisterType((*UpdateExchangeSupportedPairsRequest)(nil), "gctrpc.UpdateExchangeSupportedPairsRequest") + proto.RegisterType((*GetExchangeAssetsRequest)(nil), "gctrpc.GetExchangeAssetsRequest") + proto.RegisterType((*GetExchangeAssetsResponse)(nil), "gctrpc.GetExchangeAssetsResponse") + proto.RegisterType((*WebsocketGetInfoRequest)(nil), "gctrpc.WebsocketGetInfoRequest") + proto.RegisterType((*WebsocketGetInfoResponse)(nil), "gctrpc.WebsocketGetInfoResponse") + proto.RegisterType((*WebsocketSetEnabledRequest)(nil), "gctrpc.WebsocketSetEnabledRequest") + proto.RegisterType((*WebsocketGetSubscriptionsRequest)(nil), "gctrpc.WebsocketGetSubscriptionsRequest") + proto.RegisterType((*WebsocketSubscription)(nil), "gctrpc.WebsocketSubscription") + proto.RegisterType((*WebsocketGetSubscriptionsResponse)(nil), "gctrpc.WebsocketGetSubscriptionsResponse") + proto.RegisterType((*WebsocketSetProxyRequest)(nil), "gctrpc.WebsocketSetProxyRequest") + proto.RegisterType((*WebsocketSetURLRequest)(nil), "gctrpc.WebsocketSetURLRequest") +} + +func init() { + proto.RegisterFile("rpc.proto", fileDescriptor_77a6da22d6a3feb1) +} + +var fileDescriptor_77a6da22d6a3feb1 = []byte{ + // 6423 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x7c, 0x4d, 0x8c, 0x1c, 0xc7, + 0x75, 0x30, 0x7a, 0x66, 0xf6, 0x67, 0xde, 0xfe, 0xb2, 0xf6, 0x6f, 0x38, 0x5c, 0x72, 0xc9, 0x92, + 0x45, 0x91, 0xfa, 0x59, 0x4a, 0x94, 0x64, 0xcb, 0xf2, 0xdf, 0xb7, 0x5c, 0x4a, 0x6b, 0xda, 0xb4, + 0x44, 0xf7, 0x2e, 0x25, 0x40, 0xfe, 0xa0, 0x49, 0xcf, 0x74, 0xed, 0x6e, 0x9b, 0x3d, 0xdd, 0xa3, + 0xee, 0x9e, 0x5d, 0xae, 0x8d, 0xc0, 0x86, 0x91, 0x04, 0x01, 0x1c, 0x24, 0x08, 0x0c, 0xc3, 0x09, + 0x90, 0x53, 0x4e, 0x41, 0x2e, 0x06, 0x82, 0x1c, 0x82, 0x1c, 0x8c, 0x5c, 0x83, 0x00, 0xb9, 0x04, + 0x08, 0x7c, 0xc9, 0x29, 0x41, 0x0e, 0x41, 0x92, 0x43, 0x80, 0x5c, 0x72, 0x0a, 0xea, 0xd5, 0x4f, + 0x57, 0x75, 0xf7, 0xcc, 0xce, 0xca, 0xb4, 0x72, 0x21, 0xa7, 0x5f, 0xbd, 0x7a, 0xef, 0xd5, 0xab, + 0x57, 0xaf, 0x5e, 0xbd, 0x7a, 0xb5, 0xd0, 0x4c, 0x06, 0xbd, 0xed, 0x41, 0x12, 0x67, 0x31, 0x99, + 0x3e, 0xea, 0x65, 0xc9, 0xa0, 0xd7, 0xde, 0x3c, 0x8a, 0xe3, 0xa3, 0x90, 0xdd, 0xf1, 0x06, 0xc1, + 0x1d, 0x2f, 0x8a, 0xe2, 0xcc, 0xcb, 0x82, 0x38, 0x4a, 0x05, 0x56, 0x7b, 0x4b, 0xb6, 0xe2, 0x57, + 0x77, 0x78, 0x78, 0x27, 0x0b, 0xfa, 0x2c, 0xcd, 0xbc, 0xfe, 0x40, 0x20, 0xd0, 0x65, 0x58, 0xdc, + 0x63, 0xd9, 0x83, 0xe8, 0x30, 0x76, 0xd9, 0x27, 0x43, 0x96, 0x66, 0xf4, 0x2f, 0x1b, 0xb0, 0xa4, + 0x41, 0xe9, 0x20, 0x8e, 0x52, 0x46, 0xd6, 0x61, 0x7a, 0x38, 0xe0, 0x5d, 0x5b, 0xce, 0x75, 0xe7, + 0x56, 0xd3, 0x95, 0x5f, 0xe4, 0x0e, 0xac, 0x78, 0x27, 0x5e, 0x10, 0x7a, 0xdd, 0x90, 0x75, 0xd8, + 0xd3, 0xde, 0xb1, 0x17, 0x1d, 0xb1, 0xb4, 0x55, 0xbb, 0xee, 0xdc, 0xaa, 0xbb, 0x44, 0x37, 0xbd, + 0xa3, 0x5a, 0xc8, 0x4b, 0x70, 0x89, 0x45, 0x1c, 0xe4, 0x1b, 0xe8, 0x75, 0x44, 0x5f, 0x96, 0x0d, + 0x39, 0xf2, 0x1b, 0xb0, 0xee, 0xb3, 0x43, 0x6f, 0x18, 0x66, 0x9d, 0xc3, 0x38, 0x61, 0x4f, 0x3b, + 0x83, 0x24, 0x3e, 0x09, 0x7c, 0x96, 0xb4, 0x1a, 0x28, 0xc5, 0xaa, 0x6c, 0x7d, 0x97, 0x37, 0x3e, + 0x92, 0x6d, 0xe4, 0x2e, 0xac, 0xe9, 0x5e, 0x81, 0x97, 0x75, 0x7a, 0xc3, 0x24, 0x61, 0x51, 0xef, + 0xac, 0x35, 0x85, 0x9d, 0x56, 0x54, 0xa7, 0xc0, 0xcb, 0x76, 0x65, 0x13, 0xf9, 0x10, 0x96, 0xd3, + 0x61, 0x37, 0x3d, 0x4b, 0x33, 0xd6, 0xef, 0xa4, 0x99, 0x97, 0x0d, 0xd3, 0xd6, 0xf4, 0xf5, 0xfa, + 0xad, 0xb9, 0xbb, 0x2f, 0x6f, 0x0b, 0x3d, 0x6f, 0x17, 0x54, 0xb2, 0xbd, 0xaf, 0xf0, 0xf7, 0x11, + 0xfd, 0x9d, 0x28, 0x4b, 0xce, 0xdc, 0xa5, 0xd4, 0x86, 0x92, 0xf7, 0x60, 0x21, 0x19, 0xf4, 0x3a, + 0x2c, 0xf2, 0x07, 0x71, 0x10, 0x65, 0x69, 0x6b, 0x06, 0xa9, 0xde, 0x1e, 0x45, 0xd5, 0x1d, 0xf4, + 0xde, 0x51, 0xb8, 0x82, 0xe4, 0x7c, 0x62, 0x80, 0xda, 0xf7, 0x60, 0xb5, 0x8a, 0x31, 0x59, 0x86, + 0xfa, 0x13, 0x76, 0x26, 0x67, 0x87, 0xff, 0x24, 0xab, 0x30, 0x75, 0xe2, 0x85, 0x43, 0x86, 0x93, + 0x31, 0xeb, 0x8a, 0x8f, 0xb7, 0x6b, 0x6f, 0x39, 0xed, 0x03, 0xb8, 0x54, 0x62, 0x53, 0x41, 0xe0, + 0xb6, 0x49, 0x60, 0xee, 0xee, 0x8a, 0x12, 0xd9, 0x7d, 0xb4, 0xab, 0xfa, 0x1a, 0x54, 0xe9, 0x0d, + 0xd8, 0xda, 0x63, 0xd9, 0x6e, 0xdc, 0xef, 0x0f, 0xa3, 0xa0, 0x87, 0x46, 0xe8, 0xb2, 0xd0, 0x3b, + 0x63, 0x49, 0xaa, 0x2c, 0xeb, 0x3d, 0x58, 0xad, 0x6a, 0x27, 0x2d, 0x98, 0x91, 0x73, 0x8f, 0xfc, + 0x67, 0x5d, 0xf5, 0x49, 0x36, 0xa1, 0xd9, 0x8b, 0xa3, 0x88, 0xf5, 0x32, 0xe6, 0xcb, 0x81, 0xe4, + 0x00, 0xfa, 0x3b, 0x35, 0xb8, 0x3e, 0x9a, 0xa7, 0x34, 0xdd, 0xef, 0xc1, 0x7a, 0xcf, 0x44, 0xe8, + 0x24, 0x12, 0xa3, 0xe5, 0xe0, 0x54, 0xec, 0x1a, 0x53, 0x31, 0x96, 0xd2, 0x76, 0x65, 0xab, 0x98, + 0xa4, 0xb5, 0x5e, 0x55, 0x5b, 0xfb, 0x10, 0xda, 0xa3, 0x3b, 0x55, 0xa8, 0xfc, 0xae, 0xad, 0xf2, + 0x4d, 0x25, 0x5a, 0x15, 0x11, 0x53, 0xf7, 0x5f, 0x80, 0x8d, 0x3d, 0x16, 0xb1, 0x24, 0xe8, 0x69, + 0xe3, 0x90, 0x3a, 0xe7, 0x1a, 0xd4, 0x36, 0x29, 0x59, 0xe5, 0x00, 0xba, 0x0e, 0xab, 0x7b, 0x2c, + 0xd3, 0x9d, 0xf4, 0x4c, 0xfd, 0xc2, 0x81, 0x35, 0x6c, 0x48, 0xbb, 0xe9, 0x99, 0x68, 0x90, 0xea, + 0xfc, 0x0d, 0xb8, 0xa4, 0xbb, 0xa7, 0x6a, 0xa9, 0x08, 0x4d, 0xbe, 0x6e, 0x68, 0xb2, 0xdc, 0x33, + 0x5f, 0x30, 0xa9, 0xb9, 0x62, 0xf2, 0x75, 0x27, 0xc1, 0xed, 0x5d, 0x58, 0xab, 0x44, 0xbd, 0x88, + 0x8d, 0xd3, 0x16, 0xac, 0xef, 0xb1, 0xcc, 0x30, 0x55, 0xc3, 0x08, 0xe7, 0x0c, 0x30, 0xb7, 0xbd, + 0x34, 0xf3, 0x92, 0x2c, 0xb7, 0x3d, 0xf9, 0x49, 0x9e, 0x87, 0xc5, 0x30, 0x48, 0x33, 0x16, 0x75, + 0x3c, 0xdf, 0x4f, 0x58, 0x2a, 0xdc, 0x5a, 0xd3, 0x5d, 0x10, 0xd0, 0x1d, 0x01, 0xa4, 0x7f, 0xed, + 0x70, 0xe5, 0x17, 0x58, 0x49, 0x65, 0x3d, 0x84, 0x66, 0xbe, 0xf2, 0x85, 0x92, 0xb6, 0x0d, 0x25, + 0x55, 0xf5, 0xd9, 0x2e, 0x2c, 0xff, 0x9c, 0x40, 0xfb, 0xdb, 0xb0, 0xf8, 0xac, 0x17, 0xed, 0x5b, + 0xd0, 0x96, 0x86, 0xa3, 0xbc, 0xee, 0x7b, 0x5e, 0x9f, 0x29, 0xdb, 0x69, 0xc3, 0xac, 0x72, 0xd2, + 0x92, 0x87, 0xfe, 0xa6, 0x77, 0x60, 0x65, 0x8f, 0x65, 0xda, 0x57, 0xab, 0x2e, 0x23, 0x97, 0x32, + 0x7d, 0x03, 0x4d, 0xcd, 0xe8, 0x20, 0x75, 0xb4, 0x09, 0xcd, 0x7c, 0x27, 0x90, 0x06, 0xaa, 0x01, + 0xf4, 0x2e, 0xda, 0xa1, 0xea, 0xf5, 0xfe, 0xc1, 0x23, 0x97, 0x89, 0x6e, 0x97, 0x61, 0x36, 0xce, + 0x06, 0x9d, 0x5e, 0xec, 0x2b, 0xd9, 0x66, 0xe2, 0x6c, 0xb0, 0x1b, 0xfb, 0x4c, 0xce, 0xbd, 0xd1, + 0x47, 0xcf, 0xfd, 0x9f, 0x8a, 0xb9, 0xb2, 0x9b, 0xa4, 0x1c, 0xdf, 0x80, 0xa6, 0x22, 0xa8, 0xe6, + 0xea, 0x15, 0x63, 0xae, 0xaa, 0xfa, 0x6c, 0xbf, 0x2f, 0x38, 0xca, 0xa9, 0x9a, 0x95, 0x02, 0xa4, + 0xed, 0x2f, 0xc1, 0x82, 0xd5, 0x74, 0x9e, 0xe9, 0x36, 0xcd, 0x39, 0x79, 0x03, 0xd6, 0xef, 0x07, + 0xa9, 0xb9, 0x6d, 0x4e, 0x32, 0x1f, 0x1f, 0xc3, 0xe2, 0x23, 0x2f, 0x48, 0xd2, 0xfd, 0xe1, 0x60, + 0x10, 0xa3, 0xfd, 0xbe, 0x00, 0x4b, 0xf9, 0xde, 0x3c, 0xe0, 0x6d, 0xb2, 0xd3, 0xa2, 0x06, 0x63, + 0x0f, 0xf2, 0x1c, 0x2c, 0xa8, 0x3d, 0x59, 0xa0, 0x09, 0x91, 0xe6, 0x25, 0x10, 0x91, 0xe8, 0x8f, + 0x1a, 0x96, 0xea, 0xac, 0xe8, 0x80, 0x40, 0x23, 0xf2, 0x74, 0x6c, 0x80, 0xbf, 0x4d, 0x43, 0xa8, + 0xd9, 0x3e, 0xbd, 0x05, 0x33, 0x27, 0x2c, 0xe9, 0xc6, 0x29, 0xc3, 0x8d, 0x7f, 0xd6, 0x55, 0x9f, + 0x5c, 0x90, 0x61, 0x1a, 0x44, 0x47, 0x9d, 0xd4, 0x8b, 0xfc, 0x6e, 0xfc, 0x14, 0xb7, 0xf9, 0x59, + 0x77, 0x1e, 0x81, 0xfb, 0x02, 0x46, 0x6e, 0xc0, 0xfc, 0x71, 0x96, 0x0d, 0x3a, 0x3c, 0xfe, 0x88, + 0x87, 0x99, 0xdc, 0xd5, 0xe7, 0x38, 0xec, 0x40, 0x80, 0xf8, 0xca, 0x45, 0x94, 0x61, 0xca, 0x12, + 0xef, 0x88, 0x45, 0x59, 0x6b, 0x5a, 0xac, 0x5c, 0x0e, 0x7d, 0xac, 0x80, 0xe4, 0x2a, 0x00, 0xa2, + 0x0d, 0x92, 0xf8, 0xe9, 0x59, 0x6b, 0x46, 0x98, 0x1e, 0x87, 0x3c, 0xe2, 0x00, 0xae, 0xbf, 0xae, + 0x97, 0x32, 0x15, 0x3f, 0x04, 0x2c, 0x6d, 0xcd, 0x0a, 0xfd, 0x71, 0xf0, 0xae, 0x86, 0x92, 0x0e, + 0x0f, 0x1e, 0xa4, 0xd6, 0x3b, 0x5e, 0x9a, 0xb2, 0x2c, 0x6d, 0x35, 0xd1, 0x80, 0xde, 0xa8, 0x30, + 0xa0, 0x42, 0x10, 0x21, 0xfb, 0xed, 0x60, 0x37, 0x1d, 0x44, 0x58, 0x50, 0x1e, 0x34, 0x79, 0xc3, + 0xec, 0x98, 0x45, 0x19, 0xdf, 0x02, 0x38, 0x93, 0x41, 0xd0, 0x02, 0xd4, 0xcd, 0xb2, 0xd5, 0xb0, + 0x33, 0x08, 0xda, 0x1f, 0xf1, 0x08, 0xa1, 0x4c, 0xb5, 0xc2, 0x04, 0x5f, 0xb6, 0x7d, 0xc5, 0xba, + 0x12, 0xd6, 0xb6, 0x23, 0xd3, 0x34, 0x4f, 0x61, 0x79, 0x8f, 0x65, 0x07, 0x41, 0xef, 0x09, 0x4b, + 0x26, 0x30, 0x4a, 0x72, 0x0b, 0x1a, 0xdc, 0xa2, 0x24, 0x83, 0x55, 0xbd, 0x9d, 0xc9, 0xb0, 0x8b, + 0x33, 0x72, 0x11, 0x83, 0xcf, 0x05, 0x6a, 0xae, 0x93, 0x9d, 0x0d, 0x84, 0x5d, 0x34, 0xdd, 0x26, + 0x42, 0x0e, 0xce, 0x06, 0x8c, 0x7e, 0x00, 0xf3, 0x66, 0x27, 0xee, 0x34, 0x7c, 0x16, 0x06, 0xfd, + 0x20, 0x63, 0x89, 0x72, 0x1a, 0x1a, 0xc0, 0xed, 0x91, 0x4f, 0x91, 0xb4, 0x63, 0xfc, 0xcd, 0xd7, + 0xdb, 0x27, 0xc3, 0x38, 0x53, 0xb4, 0xc5, 0x07, 0xfd, 0x69, 0x0d, 0x16, 0xd5, 0x70, 0xa4, 0x31, + 0x2b, 0x99, 0x9d, 0x73, 0x65, 0xbe, 0x01, 0xf3, 0xa1, 0x97, 0x66, 0x9d, 0xe1, 0xc0, 0xf7, 0x54, + 0x7c, 0x52, 0x77, 0xe7, 0x38, 0xec, 0xb1, 0x00, 0x71, 0x8b, 0x56, 0xe1, 0x27, 0xae, 0x2d, 0xc9, + 0x7d, 0xbe, 0x67, 0x0e, 0x86, 0x40, 0x83, 0xf7, 0x41, 0x6b, 0x77, 0x5c, 0xfc, 0xcd, 0x61, 0xc7, + 0xc1, 0xd1, 0x31, 0x5a, 0xb7, 0xe3, 0xe2, 0x6f, 0x3e, 0x83, 0x61, 0x7c, 0x8a, 0xb6, 0xec, 0xb8, + 0xfc, 0x27, 0x87, 0x74, 0x03, 0x1f, 0x4d, 0xd7, 0x71, 0xf9, 0x4f, 0x0e, 0xf1, 0xd2, 0x27, 0x68, + 0xa8, 0x8e, 0xcb, 0x7f, 0xf2, 0xd0, 0xfd, 0x24, 0x0e, 0x87, 0x7d, 0xd6, 0x6a, 0x22, 0x50, 0x7e, + 0x91, 0x2b, 0xd0, 0x1c, 0x24, 0x41, 0x8f, 0x75, 0xbc, 0xec, 0x18, 0x8d, 0xc9, 0x71, 0x67, 0x11, + 0xb0, 0x93, 0x1d, 0xd3, 0x15, 0xb8, 0xa4, 0x27, 0x5a, 0x7b, 0xcf, 0x0f, 0x61, 0x46, 0x42, 0xc6, + 0x4e, 0xfa, 0xab, 0x30, 0x93, 0x09, 0xb4, 0x56, 0x0d, 0x57, 0x81, 0x36, 0x2c, 0x5b, 0xd3, 0xae, + 0x42, 0xa3, 0x5f, 0x03, 0x62, 0x72, 0x93, 0x13, 0x71, 0x3b, 0xa7, 0x23, 0xdc, 0xf1, 0x92, 0x4d, + 0x27, 0xcd, 0x09, 0x7c, 0x0f, 0x37, 0xa3, 0xf7, 0x13, 0x9f, 0x3b, 0x92, 0xf8, 0xc9, 0x67, 0x6a, + 0x9a, 0xdf, 0x82, 0x05, 0xcd, 0xf8, 0x41, 0xc6, 0xfa, 0x5c, 0xe1, 0x5e, 0x3f, 0x1e, 0x46, 0x19, + 0xf2, 0x74, 0x5c, 0xf9, 0xc5, 0x2d, 0x10, 0xf5, 0x8b, 0x2c, 0x1d, 0x57, 0x7c, 0x90, 0x45, 0xa8, + 0x05, 0xbe, 0x3c, 0x01, 0xd5, 0x02, 0x9f, 0xfe, 0x8f, 0x03, 0x97, 0x8c, 0x81, 0x5c, 0xd8, 0x28, + 0x4b, 0x16, 0x57, 0xab, 0xb0, 0xb8, 0xdb, 0xd0, 0xe8, 0x06, 0x3e, 0x3f, 0x78, 0x71, 0xbd, 0xae, + 0x29, 0x72, 0xd6, 0x38, 0x5c, 0x44, 0xe1, 0xa8, 0x5e, 0xfa, 0x24, 0x6d, 0x35, 0xc6, 0xa2, 0x72, + 0x94, 0xd2, 0x7a, 0x98, 0x2a, 0xaf, 0x07, 0x5b, 0x97, 0xd3, 0x45, 0x5d, 0x8a, 0x70, 0x54, 0xd3, + 0xd6, 0x96, 0xd7, 0x03, 0xc8, 0x81, 0x63, 0xa7, 0xf5, 0x8b, 0x00, 0xb1, 0xc6, 0x94, 0xf6, 0x77, + 0xb9, 0x24, 0xb4, 0x36, 0x41, 0x03, 0x99, 0x7e, 0x13, 0x43, 0x0d, 0x93, 0xb9, 0x54, 0xfe, 0x5d, + 0x8b, 0xa6, 0xb0, 0x45, 0x52, 0xa2, 0x99, 0x5a, 0xc4, 0x5e, 0x47, 0x62, 0x3b, 0xbd, 0x1e, 0x9f, + 0x7a, 0xe3, 0x74, 0x3d, 0x76, 0x0f, 0xff, 0x00, 0x66, 0x64, 0x0f, 0x69, 0x16, 0x02, 0xa1, 0x16, + 0xf8, 0xe4, 0x4b, 0x00, 0xc6, 0x3e, 0x24, 0xc6, 0x75, 0x45, 0xc9, 0x20, 0x3b, 0x29, 0x6b, 0x40, + 0x76, 0x06, 0x3a, 0x3d, 0x84, 0x95, 0x0a, 0x14, 0x2e, 0x8a, 0x3e, 0x1b, 0x4b, 0x51, 0xd4, 0x37, + 0xd9, 0x82, 0xb9, 0x2c, 0xce, 0xbc, 0xb0, 0x93, 0xef, 0x10, 0x8e, 0x0b, 0x08, 0xfa, 0x80, 0x43, + 0xd0, 0x41, 0xc5, 0xa1, 0xb0, 0x5c, 0xee, 0xa0, 0xe2, 0xd0, 0xa7, 0x1e, 0x06, 0x5e, 0xd6, 0xa0, + 0xa5, 0x0a, 0xc7, 0x4d, 0xd9, 0x4b, 0x30, 0xeb, 0x89, 0x2e, 0x6a, 0x60, 0x4b, 0x85, 0x81, 0xb9, + 0x1a, 0x81, 0x12, 0xdc, 0x81, 0x76, 0xe3, 0xe8, 0x30, 0x38, 0x52, 0xd6, 0xf1, 0x02, 0x3a, 0x2b, + 0x05, 0xcb, 0x63, 0x12, 0xdf, 0xcb, 0x3c, 0xe4, 0x36, 0xef, 0xe2, 0x6f, 0xfa, 0xdb, 0x0e, 0x2c, + 0x3f, 0x8a, 0x93, 0xec, 0x30, 0x0e, 0x83, 0x58, 0xc6, 0xef, 0x3c, 0x1c, 0x51, 0xf1, 0xbd, 0x8c, + 0x23, 0xe5, 0x27, 0xf7, 0x90, 0xbd, 0x38, 0x88, 0x84, 0xad, 0xd6, 0xa4, 0x82, 0xe2, 0x20, 0xe2, + 0xa6, 0x4a, 0xae, 0xc3, 0x9c, 0xcf, 0xd2, 0x5e, 0x12, 0x0c, 0xf8, 0x99, 0x4c, 0xba, 0x05, 0x13, + 0xc4, 0x09, 0x77, 0xbd, 0xd0, 0x8b, 0x7a, 0x4c, 0x7a, 0x76, 0xf5, 0x49, 0xd7, 0xd0, 0x5d, 0x69, + 0x49, 0x8c, 0xe3, 0xb1, 0x0d, 0x96, 0x43, 0xf9, 0x3c, 0x34, 0x07, 0x0a, 0x28, 0xcd, 0xaf, 0xa5, + 0xf7, 0xea, 0xc2, 0x70, 0xdc, 0x1c, 0x95, 0x6e, 0xf2, 0xe0, 0x3e, 0xa7, 0xb7, 0x3f, 0xec, 0xf7, + 0xbd, 0xe4, 0x4c, 0x71, 0x8b, 0xa0, 0xb1, 0x1b, 0x07, 0x11, 0x57, 0x14, 0x1f, 0x94, 0x0a, 0xde, + 0xf8, 0x6f, 0x53, 0xf4, 0x9a, 0x25, 0xba, 0xa9, 0xad, 0xba, 0xad, 0xad, 0x6b, 0x00, 0x03, 0x96, + 0xf4, 0x58, 0x94, 0x79, 0x47, 0x6a, 0xc4, 0x06, 0x84, 0x1e, 0x03, 0x79, 0xff, 0xf0, 0x30, 0x0c, + 0x22, 0xc6, 0xd9, 0x4a, 0x61, 0xc6, 0x68, 0x7f, 0xb4, 0x0c, 0x36, 0xa7, 0x7a, 0x89, 0xd3, 0xb7, + 0xe0, 0xd2, 0xfb, 0x51, 0x05, 0x23, 0x45, 0xce, 0x19, 0x47, 0xae, 0x56, 0x22, 0xf7, 0x75, 0x98, + 0x37, 0x04, 0x4f, 0xc9, 0x5b, 0xd0, 0x94, 0x32, 0xea, 0x83, 0x42, 0x5b, 0x7b, 0x83, 0xd2, 0x08, + 0xdd, 0x1c, 0x99, 0xfe, 0x91, 0x03, 0x73, 0xb9, 0x64, 0x29, 0x79, 0x03, 0xa6, 0xb8, 0xba, 0x15, + 0x95, 0x6b, 0x9a, 0x4a, 0x8e, 0xb3, 0x8d, 0xff, 0x8a, 0xb8, 0x50, 0x20, 0xb7, 0xf7, 0x01, 0x72, + 0x60, 0x45, 0x58, 0x77, 0xc7, 0x0e, 0xeb, 0x2e, 0x97, 0xa9, 0x2a, 0xd1, 0x8c, 0xc8, 0xee, 0xef, + 0x1a, 0x70, 0xa5, 0xd2, 0x58, 0xa4, 0x0d, 0xbe, 0x02, 0x73, 0x62, 0x2d, 0x70, 0x0f, 0xa0, 0x04, + 0x9e, 0xcf, 0xf3, 0x13, 0x41, 0xe4, 0x02, 0xae, 0x0d, 0x6c, 0x27, 0xaf, 0xc1, 0x02, 0x0a, 0xdb, + 0x89, 0x85, 0x42, 0xe4, 0xc2, 0xb6, 0x3b, 0xcc, 0x23, 0x8a, 0x54, 0x19, 0x19, 0xc0, 0x9a, 0xd5, + 0xa5, 0x93, 0x0a, 0x11, 0xe4, 0x26, 0xf5, 0x65, 0x23, 0x94, 0x1e, 0x25, 0xa5, 0x50, 0x96, 0x24, + 0x28, 0xdb, 0x84, 0xea, 0x56, 0x7a, 0xe5, 0x16, 0x72, 0x07, 0xe6, 0x25, 0x47, 0xd4, 0x8c, 0xdc, + 0xe2, 0x6c, 0x19, 0xe7, 0x44, 0x47, 0x44, 0x20, 0x7d, 0x58, 0x35, 0x3b, 0x68, 0x09, 0xa7, 0xb0, + 0xe3, 0x97, 0x26, 0x97, 0x30, 0x2a, 0x09, 0x48, 0x7a, 0xa5, 0x86, 0xf6, 0xff, 0x87, 0xd6, 0xa8, + 0x01, 0x55, 0x4c, 0xfb, 0x8b, 0xf6, 0xb4, 0xaf, 0x56, 0x98, 0x64, 0x6a, 0x66, 0x01, 0x3f, 0x82, + 0x8d, 0x11, 0xc2, 0x5c, 0x20, 0xad, 0x60, 0x58, 0xaa, 0x69, 0x4d, 0xff, 0xec, 0x40, 0x7b, 0xc7, + 0xf7, 0x4b, 0xce, 0x29, 0x4f, 0x12, 0x7c, 0xc6, 0x2e, 0x97, 0xdc, 0x81, 0x95, 0xfc, 0x8c, 0x96, + 0xe7, 0x1b, 0xc4, 0xe1, 0x91, 0xe8, 0xa6, 0x3c, 0xf7, 0x7c, 0x83, 0x1b, 0x47, 0xe8, 0x77, 0xd2, + 0x2c, 0xe6, 0xc7, 0x45, 0x8c, 0x55, 0x66, 0xb9, 0x39, 0x84, 0xfe, 0xbe, 0x00, 0xd1, 0xa7, 0x70, + 0xd5, 0x65, 0xfd, 0xf8, 0x84, 0x7d, 0xd6, 0xe3, 0xa4, 0x6d, 0x68, 0xed, 0x31, 0x3b, 0xed, 0xad, + 0x63, 0xa5, 0xff, 0x70, 0x60, 0xc1, 0x4e, 0x88, 0x3f, 0xab, 0xe3, 0xf9, 0xcb, 0x40, 0x12, 0x96, + 0x66, 0x9d, 0x41, 0x1c, 0x86, 0xfc, 0x94, 0xee, 0xb3, 0xd0, 0x3b, 0x93, 0xa9, 0xf8, 0x65, 0xde, + 0xf2, 0x48, 0x34, 0xdc, 0xe7, 0x70, 0xb2, 0x01, 0x33, 0xde, 0x20, 0xe8, 0x70, 0x43, 0x12, 0x5a, + 0x9e, 0xf6, 0x06, 0xc1, 0x37, 0xd9, 0x19, 0xa1, 0xb0, 0x20, 0x1b, 0x3a, 0x21, 0x3b, 0x61, 0x21, + 0xaa, 0xb6, 0xee, 0xce, 0x89, 0xe6, 0x87, 0x1c, 0x44, 0x6e, 0xc3, 0xf2, 0x20, 0x09, 0xb8, 0x45, + 0xe6, 0x39, 0xff, 0x19, 0x94, 0x66, 0x49, 0xc2, 0xd5, 0xe8, 0xe8, 0x77, 0xe0, 0x72, 0x85, 0x2e, + 0xa4, 0xdb, 0xfa, 0x2a, 0x2c, 0xd9, 0x37, 0x07, 0xca, 0x75, 0xe9, 0x40, 0xd6, 0xea, 0xe8, 0x2e, + 0x1e, 0x5a, 0x74, 0x64, 0x40, 0x8a, 0x38, 0xae, 0x97, 0xe9, 0x34, 0x17, 0xfd, 0x04, 0x56, 0x73, + 0xe0, 0x6e, 0x1c, 0x9d, 0xb0, 0x24, 0xe5, 0x06, 0x48, 0xa0, 0x71, 0x98, 0xc4, 0x2a, 0xd1, 0x8a, + 0xbf, 0x79, 0x28, 0x97, 0xc5, 0x72, 0x92, 0x6b, 0x59, 0xcc, 0x71, 0x12, 0x2f, 0x53, 0x1b, 0x17, + 0xfe, 0xe6, 0xd6, 0x16, 0x20, 0x11, 0xd6, 0xc1, 0x36, 0x61, 0xbd, 0x73, 0x12, 0xc6, 0xb9, 0xd0, + 0x0f, 0x30, 0xa2, 0x34, 0x45, 0x91, 0x63, 0xfc, 0x0a, 0xcc, 0x89, 0x31, 0xf2, 0x9e, 0x6a, 0x7c, + 0x9b, 0xd6, 0xf8, 0x0a, 0x62, 0xba, 0x70, 0xa8, 0xa1, 0xf4, 0xe7, 0x75, 0x98, 0xc7, 0x20, 0xf6, + 0x3e, 0xcb, 0xbc, 0x20, 0x1c, 0x1f, 0x5e, 0x8b, 0xb0, 0xb4, 0xa6, 0xc3, 0xd2, 0xe7, 0x60, 0xc1, + 0xcc, 0x91, 0x9c, 0xa9, 0xf3, 0xad, 0x91, 0x21, 0x39, 0x23, 0xcf, 0xc3, 0x22, 0x9e, 0xb6, 0x73, + 0x2c, 0x61, 0x33, 0x0b, 0x08, 0xd5, 0x68, 0xf6, 0xd9, 0x60, 0xaa, 0x70, 0x36, 0xe0, 0xcd, 0x18, + 0x5f, 0x77, 0xd2, 0xc0, 0xd7, 0x47, 0x07, 0x84, 0xec, 0x07, 0xbe, 0xd1, 0x8c, 0xbd, 0x67, 0x8c, + 0x66, 0xec, 0xcd, 0x8f, 0x45, 0x09, 0x13, 0x17, 0x00, 0x78, 0x8f, 0x35, 0x8b, 0x46, 0x37, 0xaf, + 0x80, 0x07, 0x41, 0x1f, 0x6f, 0xb9, 0x64, 0x42, 0xbb, 0x29, 0x2c, 0x56, 0x7c, 0xe5, 0x27, 0x37, + 0x30, 0x4f, 0x6e, 0xf9, 0x39, 0x6f, 0xce, 0x3a, 0xe7, 0x6d, 0xc1, 0x5c, 0x3c, 0x60, 0x51, 0x47, + 0x9e, 0xba, 0xe7, 0x45, 0x40, 0xc1, 0x41, 0x1f, 0x88, 0x93, 0xf7, 0x32, 0xd4, 0x0f, 0x19, 0x6b, + 0x2d, 0x88, 0x33, 0xfa, 0x21, 0xe3, 0x2b, 0x6b, 0x3a, 0x4b, 0x3c, 0x9f, 0xa5, 0xad, 0x45, 0x9c, + 0x3d, 0xed, 0xbc, 0x0f, 0x38, 0xf4, 0xeb, 0x01, 0x77, 0x42, 0x67, 0xae, 0xc4, 0xa1, 0xff, 0xe4, + 0xc0, 0xbc, 0xd9, 0x50, 0x1e, 0x9c, 0x53, 0x31, 0xb8, 0xe2, 0xd4, 0xe9, 0x41, 0xd5, 0xab, 0x07, + 0xd5, 0xb0, 0x06, 0x65, 0x1a, 0xc5, 0x54, 0xc1, 0x28, 0xc6, 0x1f, 0xea, 0x0a, 0x13, 0x37, 0x53, + 0x9c, 0x38, 0xa9, 0x8d, 0x59, 0xad, 0x0d, 0x99, 0x65, 0x42, 0x9b, 0x4c, 0x27, 0x39, 0xca, 0xdb, + 0xfc, 0x6b, 0x45, 0xfe, 0xea, 0xec, 0x5c, 0x3f, 0xef, 0xec, 0x4c, 0x77, 0xf0, 0x20, 0xa1, 0x18, + 0xcb, 0xe5, 0xf5, 0x32, 0x4c, 0xa3, 0xb0, 0x6a, 0x65, 0xad, 0x5a, 0x27, 0x3f, 0xb9, 0x68, 0x5c, + 0x89, 0x43, 0xbf, 0x8e, 0x77, 0xa7, 0xd8, 0x34, 0x89, 0xe8, 0x97, 0x61, 0x56, 0xe8, 0x46, 0x4f, + 0xcd, 0x0c, 0x7e, 0x3f, 0xf0, 0xe9, 0x2f, 0x1d, 0x20, 0xfb, 0xc3, 0x6e, 0x3f, 0x98, 0x9c, 0xda, + 0xe4, 0x39, 0x0d, 0x02, 0x0d, 0x9c, 0x0d, 0xb1, 0x5c, 0xf1, 0x77, 0x61, 0x05, 0x35, 0x8a, 0x2b, + 0x28, 0xb7, 0x8c, 0xa9, 0xea, 0xb4, 0xc6, 0xb4, 0x69, 0x47, 0x7c, 0x83, 0x0b, 0x03, 0x16, 0x65, + 0x1d, 0x99, 0x9f, 0xe2, 0x1b, 0x1c, 0x02, 0x1e, 0xf8, 0x74, 0x1f, 0x56, 0xac, 0x91, 0x49, 0x4d, + 0xdf, 0x80, 0x79, 0x21, 0xc0, 0x20, 0xf4, 0x7a, 0xfa, 0x02, 0x61, 0x0e, 0x61, 0x8f, 0x10, 0x34, + 0x4e, 0x5f, 0xbf, 0xeb, 0xc0, 0xea, 0x7e, 0xd0, 0x1f, 0x86, 0x5e, 0xc6, 0x7e, 0x0d, 0x1a, 0xcb, + 0x87, 0x5f, 0xb7, 0x86, 0xaf, 0x34, 0xd9, 0xc8, 0x35, 0x49, 0xff, 0xcb, 0x81, 0xb5, 0x82, 0x28, + 0x3a, 0x8c, 0xb6, 0x8d, 0x69, 0x44, 0x3e, 0x45, 0x22, 0x19, 0x4c, 0x6b, 0x16, 0xd3, 0xe7, 0x60, + 0xa1, 0x1f, 0x44, 0x41, 0x7f, 0xd8, 0xef, 0x98, 0x6b, 0x78, 0x5e, 0x02, 0x1f, 0xe1, 0x14, 0x70, + 0x24, 0xef, 0xa9, 0x81, 0xd4, 0x90, 0x48, 0x02, 0x28, 0x90, 0x5e, 0x85, 0xd5, 0xfc, 0xa8, 0xd3, + 0x39, 0xf2, 0x82, 0xa8, 0x13, 0xc6, 0x69, 0x2a, 0xe7, 0x98, 0xe4, 0x6d, 0x7b, 0x5e, 0x10, 0x3d, + 0x8c, 0xd3, 0xd4, 0x70, 0x92, 0xd3, 0xa6, 0x93, 0xa4, 0x7f, 0xe0, 0xc0, 0xf2, 0x87, 0xc7, 0x5e, + 0xc8, 0xee, 0xc5, 0xfd, 0xee, 0xb3, 0xd5, 0xfd, 0x0d, 0x98, 0x17, 0xa9, 0xca, 0xcc, 0x4b, 0x8e, + 0x98, 0x9a, 0x81, 0x39, 0x84, 0x1d, 0x20, 0xa8, 0x72, 0x1a, 0xfe, 0xd3, 0x01, 0xb2, 0xcb, 0xa3, + 0xbf, 0x70, 0x62, 0x7b, 0xe0, 0xae, 0x44, 0xa4, 0x1a, 0x72, 0x0b, 0x6b, 0x4a, 0xc8, 0x03, 0xdb, + 0xfc, 0xea, 0x96, 0xf9, 0xe9, 0xd1, 0x34, 0x2e, 0x98, 0x4f, 0x2c, 0xed, 0x73, 0xcf, 0xc3, 0xe2, + 0xa9, 0x17, 0x86, 0x2c, 0xd3, 0xd7, 0x8e, 0xf2, 0xf2, 0x42, 0x40, 0x55, 0xda, 0x42, 0x0d, 0x78, + 0xc6, 0x18, 0xf0, 0x1b, 0xb0, 0x2e, 0xc6, 0xbb, 0x13, 0x86, 0x13, 0xbb, 0x4f, 0xfa, 0x27, 0x35, + 0xd8, 0x28, 0x75, 0xd3, 0xf1, 0x93, 0x6d, 0xaf, 0x37, 0xf5, 0xb8, 0xaa, 0x3b, 0x6c, 0xcb, 0x4f, + 0xd9, 0xab, 0xfd, 0x37, 0x0e, 0x4c, 0x0b, 0xd0, 0x58, 0xb5, 0x7f, 0xa4, 0x56, 0xbe, 0xb4, 0x2c, + 0x71, 0x5a, 0xfc, 0xc2, 0x64, 0xcc, 0xc4, 0x7f, 0xe6, 0x9d, 0xb2, 0x70, 0x19, 0xf2, 0x3a, 0xf9, + 0xab, 0xb0, 0x5c, 0x44, 0xb8, 0xd0, 0x75, 0x9c, 0xc8, 0x38, 0xbd, 0x73, 0xc2, 0x8c, 0x3b, 0xe4, + 0x5f, 0x38, 0xb0, 0xb4, 0x1b, 0x47, 0x7e, 0xc0, 0x77, 0xd7, 0x47, 0x5e, 0xe2, 0xf5, 0x53, 0x59, + 0xaa, 0x20, 0x40, 0xea, 0x4a, 0x42, 0x03, 0x46, 0x24, 0x7f, 0xaf, 0x02, 0xf4, 0x8e, 0x59, 0xef, + 0x49, 0x47, 0x66, 0x63, 0x45, 0x7d, 0x03, 0x87, 0xdc, 0x0b, 0xfc, 0x94, 0xbc, 0x02, 0x2b, 0x79, + 0x73, 0xc7, 0x8b, 0xfc, 0x8e, 0x4c, 0xc5, 0xe2, 0xcd, 0x8f, 0xc6, 0xdb, 0x89, 0xfc, 0x9d, 0xf4, + 0x49, 0xca, 0x83, 0x66, 0x9d, 0x81, 0xec, 0x58, 0xbe, 0x7a, 0x49, 0xc3, 0x77, 0x10, 0x4c, 0xff, + 0xdb, 0xc1, 0xad, 0x4e, 0x8d, 0x4a, 0xce, 0x76, 0x9e, 0x74, 0xc4, 0x5c, 0xb4, 0x35, 0x65, 0xb5, + 0xc2, 0x94, 0x11, 0x68, 0x04, 0x19, 0xeb, 0xab, 0x1d, 0x84, 0xff, 0x26, 0xf7, 0x60, 0x59, 0x8f, + 0xb8, 0x33, 0x40, 0xb5, 0xc8, 0xf5, 0xb0, 0x91, 0x1f, 0xaa, 0x2d, 0xad, 0xb9, 0x4b, 0xbd, 0x82, + 0x1a, 0xd5, 0x3a, 0x9a, 0x9a, 0xc8, 0x23, 0xf7, 0x50, 0xdb, 0xd2, 0x11, 0x89, 0x2f, 0x21, 0x35, + 0xeb, 0x0d, 0x33, 0xe6, 0xcb, 0x33, 0x83, 0xfe, 0xa6, 0xff, 0xea, 0xc0, 0xd2, 0x8e, 0xef, 0xe3, + 0xb8, 0x27, 0xf1, 0x07, 0x6a, 0x94, 0xb5, 0x73, 0x46, 0x59, 0xff, 0x94, 0xa3, 0xfc, 0x95, 0xbd, + 0xc5, 0x08, 0x25, 0x50, 0x0a, 0xcb, 0xf9, 0x38, 0xab, 0xa7, 0x97, 0x7e, 0x0e, 0x88, 0x38, 0xbf, + 0x5a, 0xea, 0x28, 0x62, 0xbd, 0x0b, 0xb7, 0xf6, 0x58, 0xb6, 0x9b, 0x9c, 0x0d, 0xb2, 0x58, 0x05, + 0xf0, 0xf7, 0xd9, 0x20, 0x4e, 0x03, 0xe5, 0x8b, 0xd8, 0x44, 0x6e, 0xe6, 0x6f, 0x1d, 0xb8, 0x3d, + 0x01, 0x21, 0x29, 0xeb, 0xc7, 0xe5, 0x24, 0xdb, 0xff, 0x33, 0x0b, 0x75, 0x26, 0xa2, 0xb2, 0xad, + 0x21, 0xb2, 0x96, 0x42, 0x93, 0x6c, 0x7f, 0x19, 0x16, 0xed, 0xc6, 0x0b, 0xf9, 0x84, 0x10, 0x6e, + 0x9e, 0x23, 0xc4, 0x24, 0xc6, 0x75, 0x13, 0x16, 0x7b, 0x16, 0x09, 0xc9, 0xa8, 0x00, 0xa5, 0xbb, + 0xf0, 0xc2, 0xb9, 0xdc, 0xa4, 0xda, 0x46, 0x66, 0x1c, 0xe8, 0xcf, 0x1d, 0x58, 0xf9, 0x30, 0xc8, + 0x8e, 0xfd, 0xc4, 0x3b, 0x7d, 0x37, 0xf0, 0x26, 0xb2, 0x7e, 0xf3, 0x82, 0xa0, 0x56, 0xb8, 0x20, + 0x18, 0x15, 0x0f, 0x15, 0x92, 0x17, 0x8d, 0x72, 0x92, 0xe6, 0x26, 0x2c, 0x75, 0xbd, 0xe8, 0x49, + 0xc7, 0xd8, 0x68, 0x85, 0x59, 0x2f, 0x70, 0xb0, 0xba, 0x3d, 0xf0, 0xe9, 0x3f, 0x3a, 0xb0, 0xa6, + 0x24, 0x16, 0x83, 0x9f, 0x44, 0x66, 0x43, 0x03, 0x35, 0x3b, 0xe7, 0xb2, 0x05, 0x73, 0xf2, 0x67, + 0x27, 0xf3, 0x8e, 0xa4, 0xe3, 0x02, 0x09, 0x3a, 0xf0, 0x8e, 0xac, 0xe1, 0x36, 0x46, 0x0e, 0xd7, + 0x8e, 0x7e, 0xe5, 0xe9, 0x65, 0x3a, 0x3f, 0xcb, 0x15, 0x14, 0x30, 0x53, 0xce, 0xde, 0xbc, 0x0d, + 0xcb, 0x6a, 0x5c, 0x15, 0x6b, 0x53, 0x9c, 0xce, 0xf2, 0x28, 0xab, 0x66, 0x45, 0x59, 0x2f, 0x43, + 0x5b, 0xf5, 0xf5, 0x42, 0x5c, 0xb7, 0xf7, 0xce, 0x1e, 0xdc, 0x2f, 0xaf, 0x5d, 0xa4, 0x42, 0x0f, + 0xe0, 0x4a, 0x25, 0xb6, 0x64, 0xfa, 0x26, 0x4c, 0x31, 0x0e, 0x94, 0x21, 0xd8, 0x96, 0x5a, 0x60, + 0x85, 0x3e, 0xfa, 0xb6, 0x4c, 0x60, 0x53, 0x06, 0x37, 0x0a, 0x18, 0xe9, 0xbd, 0xb3, 0x0b, 0xd4, + 0xaa, 0x54, 0x1d, 0x45, 0xf1, 0xea, 0x1e, 0xe7, 0x64, 0xca, 0x15, 0x1f, 0xf4, 0x0c, 0xae, 0x96, + 0xd9, 0xdc, 0xf7, 0xb2, 0x89, 0x58, 0xac, 0xc2, 0x14, 0xd6, 0x71, 0xa9, 0xb5, 0x8b, 0x1f, 0x7c, + 0xb6, 0x58, 0xa4, 0x42, 0x37, 0xfe, 0x33, 0x67, 0xdd, 0x30, 0x59, 0x7f, 0x07, 0xe8, 0xb8, 0x11, + 0x96, 0xd5, 0x57, 0xbf, 0x80, 0xfa, 0x7e, 0x5a, 0x83, 0x8d, 0x11, 0x28, 0x25, 0xcd, 0xbc, 0x6d, + 0x0c, 0x51, 0xec, 0x31, 0xd7, 0x8a, 0x5c, 0x42, 0x25, 0x97, 0xa0, 0x94, 0xab, 0xe0, 0x2d, 0x98, + 0x49, 0x84, 0xa6, 0xe4, 0x36, 0x73, 0xad, 0x2c, 0xa0, 0x54, 0xa5, 0xe8, 0xaa, 0xd0, 0xc9, 0x17, + 0x01, 0x30, 0x75, 0xc0, 0xfc, 0x8e, 0x97, 0xc9, 0x9d, 0xb8, 0xbd, 0x2d, 0x2a, 0x89, 0xb7, 0x55, + 0x25, 0xf1, 0xf6, 0x81, 0xaa, 0x24, 0x76, 0x9b, 0x12, 0x7b, 0x07, 0xbb, 0xca, 0xeb, 0x5f, 0xde, + 0x75, 0xfa, 0xfc, 0xae, 0x12, 0x7b, 0x27, 0xa3, 0x07, 0xb0, 0x5e, 0x3d, 0xa6, 0xca, 0x04, 0x66, + 0x51, 0x53, 0xf9, 0x82, 0xa9, 0x5b, 0x0b, 0xe6, 0xdf, 0x9c, 0x9c, 0xac, 0x3d, 0xde, 0xb1, 0xee, + 0xed, 0xfc, 0x5c, 0xf3, 0xa8, 0x4c, 0x09, 0x81, 0x86, 0xde, 0xaa, 0xa7, 0x5c, 0xfc, 0x4d, 0xee, + 0x40, 0xe3, 0x30, 0xd0, 0xfa, 0xd0, 0xf7, 0xb6, 0xdc, 0x0f, 0x17, 0x2d, 0x01, 0x11, 0xc9, 0x9b, + 0x30, 0x2d, 0x36, 0x01, 0xf4, 0x1f, 0x73, 0x77, 0xaf, 0xea, 0x08, 0x01, 0xa1, 0xc5, 0x4e, 0x12, + 0x99, 0xfe, 0x95, 0x03, 0x2b, 0x15, 0x44, 0xf9, 0x69, 0x1c, 0x5d, 0xae, 0xa1, 0xc5, 0x59, 0x0e, + 0x78, 0x8f, 0x6b, 0xf2, 0x06, 0xcc, 0x2b, 0x57, 0x8c, 0xed, 0x42, 0x15, 0x73, 0x12, 0x86, 0x28, + 0xcf, 0xc3, 0xa2, 0x46, 0x19, 0xf6, 0xbb, 0x4c, 0xd5, 0xb1, 0x2c, 0x28, 0x24, 0x04, 0x62, 0x39, + 0x4a, 0xda, 0x95, 0xbe, 0x93, 0xff, 0xc4, 0x65, 0x78, 0x1a, 0x1c, 0xaa, 0x2a, 0x2d, 0xf1, 0x81, + 0x51, 0x55, 0xd7, 0x53, 0x21, 0x0b, 0xfe, 0xa6, 0x3e, 0xac, 0x55, 0x8e, 0x6d, 0x4c, 0x12, 0xbd, + 0xe0, 0xd0, 0x6b, 0x25, 0x87, 0x2e, 0x9d, 0x73, 0x3d, 0x4f, 0x2d, 0xbd, 0x86, 0x45, 0x6c, 0x0f, + 0xe3, 0xa3, 0xa3, 0x3c, 0x75, 0x23, 0x8d, 0x7e, 0x1d, 0xa6, 0x43, 0x84, 0xab, 0x12, 0x77, 0xf1, + 0x45, 0x23, 0xcc, 0xb5, 0x17, 0xba, 0xe4, 0x97, 0xcc, 0x41, 0x74, 0x18, 0xcb, 0x4c, 0x05, 0xfe, + 0xe6, 0x43, 0xf6, 0x59, 0x77, 0x78, 0xa4, 0x6a, 0x52, 0xf1, 0x83, 0x63, 0x9e, 0x7a, 0x49, 0x24, + 0x63, 0x7c, 0xfc, 0xcd, 0x31, 0x59, 0x92, 0xc4, 0x89, 0x0c, 0xe8, 0xc5, 0x07, 0xdd, 0x83, 0x8d, + 0xfd, 0x8b, 0x89, 0x88, 0x4e, 0x0c, 0x33, 0xe9, 0xd2, 0xd9, 0xe1, 0x07, 0xfd, 0xa6, 0x55, 0xb0, + 0x87, 0x45, 0x5d, 0x13, 0x7a, 0x4e, 0x0c, 0x2f, 0x15, 0x31, 0xfc, 0xa0, 0xbf, 0x74, 0x50, 0x0d, + 0x05, 0x6a, 0xba, 0x26, 0xb8, 0x5c, 0x00, 0x27, 0x62, 0xb6, 0x37, 0x2b, 0x0a, 0xe0, 0xac, 0xbe, + 0x93, 0x55, 0xc0, 0xfd, 0x5a, 0x8b, 0xda, 0x7e, 0xe6, 0xc0, 0xfa, 0xbe, 0x2d, 0xde, 0x33, 0xc8, + 0x3a, 0xbe, 0x08, 0x53, 0xa2, 0x98, 0xb2, 0x6e, 0x67, 0x0d, 0xad, 0x10, 0x5f, 0xa0, 0xf0, 0x79, + 0x15, 0xb7, 0x2f, 0xd2, 0x12, 0xe4, 0x17, 0xfd, 0xa1, 0x83, 0x77, 0x1b, 0x3a, 0x37, 0xb4, 0x9f, + 0x25, 0xcc, 0xeb, 0x7f, 0xa6, 0xd5, 0x4d, 0x5f, 0x83, 0x1b, 0x66, 0xf1, 0xeb, 0x85, 0x25, 0xa1, + 0xbf, 0x89, 0x35, 0x21, 0xa2, 0x62, 0xeb, 0xff, 0x40, 0xfe, 0x2f, 0xc3, 0x35, 0x43, 0xfe, 0x0b, + 0x8a, 0x41, 0xff, 0xd8, 0xc1, 0xfb, 0x9f, 0x9d, 0xa1, 0x1f, 0x64, 0xd6, 0x21, 0xe9, 0x2a, 0x00, + 0x46, 0x14, 0x1d, 0xbe, 0x79, 0xe9, 0xb2, 0x7a, 0x0e, 0xe1, 0x01, 0x0a, 0xb9, 0x0c, 0xb3, 0x2c, + 0xf2, 0x45, 0xa3, 0x8c, 0x42, 0x59, 0xe4, 0xab, 0x26, 0x91, 0xea, 0xe8, 0x9e, 0x59, 0x29, 0xa4, + 0x7b, 0x67, 0xd5, 0xb1, 0x08, 0x37, 0x8e, 0xf8, 0xf0, 0x90, 0x2f, 0x48, 0xb1, 0xa3, 0xc8, 0x2f, + 0xba, 0x2b, 0x2a, 0x8c, 0x0c, 0xd1, 0xe4, 0x6a, 0x7c, 0x11, 0xa6, 0x31, 0xd0, 0x28, 0x95, 0x2a, + 0x19, 0xb8, 0x12, 0x83, 0xfe, 0xbb, 0xb0, 0x30, 0x71, 0x91, 0x10, 0xf4, 0x76, 0xbd, 0xc8, 0x0f, + 0x27, 0x3a, 0xce, 0x3d, 0xb3, 0x19, 0xca, 0x23, 0xb5, 0x06, 0x1e, 0x39, 0xed, 0x48, 0x4d, 0x94, + 0x90, 0x61, 0xa4, 0xf6, 0x1c, 0x2c, 0x64, 0x41, 0x9f, 0x75, 0x82, 0x28, 0x63, 0xc9, 0x89, 0xa7, + 0xae, 0x0d, 0xe7, 0x39, 0xf0, 0x81, 0x84, 0x71, 0x5e, 0xec, 0x69, 0x47, 0x85, 0x3d, 0xe2, 0xf4, + 0xdf, 0x64, 0x4f, 0x55, 0xde, 0xe6, 0xef, 0x1d, 0x2c, 0x89, 0x29, 0x0d, 0x77, 0x82, 0x2a, 0xa5, + 0xc9, 0xc7, 0xab, 0x07, 0x54, 0xaf, 0x18, 0x50, 0x23, 0x1f, 0x50, 0x1b, 0x66, 0xad, 0xb1, 0x34, + 0x5d, 0xfd, 0x4d, 0x6e, 0xc2, 0x74, 0x0f, 0x85, 0x93, 0xb5, 0x05, 0x8b, 0x46, 0x2a, 0xcc, 0x0f, + 0x99, 0x2b, 0x5b, 0xe9, 0x6f, 0x39, 0x30, 0x2d, 0x40, 0x18, 0x77, 0xe4, 0x77, 0x3f, 0xf8, 0x5b, + 0x55, 0x8c, 0xd6, 0xf2, 0x8a, 0x51, 0x55, 0x57, 0x5a, 0x37, 0xea, 0x4a, 0x09, 0x34, 0xe2, 0x01, + 0x8b, 0x54, 0xfd, 0x29, 0xff, 0xcd, 0x07, 0xd1, 0x0b, 0xe3, 0x94, 0xc9, 0xe3, 0x8e, 0xf8, 0x30, + 0x6a, 0x49, 0xa7, 0xcd, 0x5a, 0x52, 0xfa, 0x14, 0x20, 0x37, 0x2e, 0x1d, 0x01, 0xc9, 0x70, 0x0d, + 0x23, 0xa0, 0x6b, 0x00, 0x81, 0xcf, 0xa2, 0x2c, 0x38, 0x0c, 0x98, 0xaa, 0x49, 0x34, 0x20, 0x7c, + 0x97, 0xef, 0xb3, 0x34, 0x55, 0x05, 0x3d, 0x4d, 0x57, 0x7d, 0x92, 0x4d, 0x68, 0xea, 0x37, 0x6b, + 0xea, 0x56, 0x42, 0x03, 0x68, 0x17, 0x9a, 0x7b, 0xbb, 0x07, 0xfb, 0x18, 0x95, 0x71, 0xc6, 0x8f, + 0x1f, 0x3f, 0xb8, 0xaf, 0x18, 0xf3, 0xdf, 0x3a, 0x76, 0xac, 0x19, 0xb1, 0x23, 0xe1, 0x73, 0x99, + 0x1d, 0xab, 0xdc, 0x15, 0xff, 0xcd, 0xd7, 0x65, 0xc4, 0x9e, 0x66, 0x9d, 0x64, 0xa8, 0x0e, 0xad, + 0x33, 0xfc, 0xdb, 0x1d, 0x46, 0xf4, 0x3e, 0x6c, 0x68, 0x1e, 0xef, 0x88, 0x4c, 0x92, 0x5a, 0x21, + 0xb7, 0x61, 0x5a, 0x44, 0x84, 0xb2, 0x32, 0xf3, 0x92, 0xde, 0xef, 0x54, 0x07, 0x57, 0x22, 0xd0, + 0x1d, 0x58, 0xd5, 0xc0, 0xfd, 0x2c, 0x1e, 0x7c, 0x0a, 0x12, 0x97, 0x0d, 0x41, 0x38, 0x89, 0x9d, + 0x50, 0x05, 0xb4, 0xf8, 0xe6, 0x21, 0x6f, 0xe2, 0x91, 0xaf, 0x6a, 0x31, 0x3b, 0x3d, 0x0c, 0xd2, + 0xcc, 0xe8, 0xf4, 0x67, 0x8e, 0xd1, 0xeb, 0xf1, 0x20, 0x8c, 0x3d, 0x5f, 0x49, 0xb5, 0x05, 0x73, + 0x82, 0xa9, 0x19, 0x33, 0x82, 0x00, 0x61, 0x48, 0x98, 0x23, 0x60, 0x99, 0x5d, 0xcd, 0x44, 0xb8, + 0xef, 0x65, 0x9e, 0x2e, 0xc0, 0xab, 0xe7, 0x05, 0x78, 0xdc, 0xe4, 0xbd, 0xa4, 0x77, 0x1c, 0x9c, + 0x30, 0x5f, 0x6e, 0x75, 0xfa, 0x9b, 0xcf, 0x73, 0x7c, 0xc2, 0x92, 0xd3, 0x24, 0xc8, 0x84, 0xd5, + 0xcd, 0xba, 0x39, 0x80, 0xee, 0x41, 0x3b, 0xd7, 0x07, 0xf3, 0x7c, 0xf5, 0xeb, 0xc2, 0x3a, 0xbc, + 0x07, 0x6b, 0x1a, 0xf8, 0xed, 0x21, 0xd3, 0xf5, 0x70, 0x17, 0xa1, 0xf1, 0x0d, 0x68, 0x69, 0xe0, + 0xce, 0x30, 0x8b, 0x1f, 0x1a, 0x8a, 0x5b, 0xb7, 0xc8, 0x34, 0x55, 0x9f, 0xc2, 0x81, 0x7e, 0x56, + 0x9f, 0x4f, 0x3e, 0xb6, 0xe6, 0x54, 0x4c, 0x5c, 0xfe, 0xe8, 0x52, 0xbf, 0xaf, 0x32, 0xaf, 0xa3, + 0x5f, 0x82, 0x19, 0x41, 0x54, 0x25, 0xca, 0x2b, 0x44, 0x55, 0x18, 0x34, 0x36, 0xa6, 0x58, 0x8e, + 0xf7, 0x1c, 0xf2, 0xb9, 0x22, 0x6a, 0xe7, 0x28, 0xc2, 0x9a, 0xe3, 0xa6, 0x2c, 0xb2, 0xfc, 0x0a, + 0x2c, 0xc9, 0x27, 0x45, 0xe7, 0x72, 0x52, 0xdd, 0x6b, 0x46, 0xf7, 0x1e, 0x86, 0xbf, 0x6a, 0xc3, + 0xc6, 0x58, 0xef, 0x53, 0x47, 0xad, 0x46, 0x60, 0x55, 0xb7, 0x02, 0xab, 0x47, 0xd0, 0x36, 0x99, + 0x84, 0xe1, 0xc4, 0xd1, 0x71, 0x4e, 0xb1, 0x66, 0x51, 0xdc, 0x81, 0xe7, 0x44, 0x8d, 0xb3, 0x22, + 0xaa, 0x43, 0xcd, 0x49, 0x49, 0xd3, 0xcf, 0x5b, 0x11, 0xb6, 0x88, 0x72, 0x27, 0xe9, 0xf7, 0x3a, + 0x6e, 0xe1, 0xc5, 0x7e, 0xb9, 0xea, 0x75, 0x40, 0x2e, 0xf2, 0xc3, 0xf8, 0x45, 0xdf, 0x84, 0x8d, + 0x0f, 0x59, 0x37, 0x8d, 0x7b, 0x4f, 0x58, 0x66, 0xbf, 0xff, 0x1d, 0xcb, 0xeb, 0x27, 0x35, 0x68, + 0x95, 0xfb, 0x4d, 0xb0, 0x7d, 0xe2, 0x33, 0x44, 0xa9, 0x11, 0xf5, 0x90, 0x53, 0x03, 0xcc, 0x6a, + 0xa4, 0xba, 0x5d, 0x8d, 0xf4, 0x05, 0xd8, 0xb0, 0x9f, 0xbe, 0xe4, 0x54, 0x84, 0x03, 0x59, 0xb7, + 0x9a, 0xf3, 0xd7, 0x4f, 0x9f, 0x83, 0x05, 0xab, 0x45, 0xba, 0x14, 0x1b, 0xc8, 0xbd, 0x58, 0x32, + 0x8c, 0xa2, 0x20, 0x3a, 0xea, 0x0c, 0x13, 0xb5, 0x0d, 0x83, 0x04, 0x3d, 0x4e, 0x42, 0x1e, 0x75, + 0xe0, 0xf3, 0x20, 0x7d, 0x19, 0x27, 0xf2, 0x79, 0xf3, 0x08, 0x54, 0x4f, 0x00, 0x1f, 0x41, 0x5b, + 0x2b, 0x85, 0xdb, 0x95, 0x90, 0xfd, 0x57, 0x31, 0xa7, 0xaf, 0xc2, 0x75, 0x53, 0xcd, 0xfb, 0xc3, + 0xae, 0x4e, 0x3c, 0x4c, 0x64, 0x13, 0xdf, 0x87, 0xb5, 0x5c, 0x22, 0xa3, 0x33, 0xd7, 0x34, 0x47, + 0x89, 0x58, 0xa8, 0x4e, 0xd3, 0xf2, 0x73, 0x6c, 0x36, 0x44, 0xaf, 0xae, 0x7a, 0x61, 0x75, 0x19, + 0x97, 0x3c, 0x4d, 0x57, 0x7e, 0xf1, 0xa0, 0xe4, 0xc6, 0x18, 0xe9, 0x27, 0xb0, 0x96, 0x5d, 0x58, + 0x48, 0xcd, 0x4e, 0xd2, 0xcf, 0xe9, 0x2c, 0x48, 0xe5, 0xd8, 0x5c, 0xbb, 0x0f, 0x7d, 0x68, 0x98, + 0xea, 0x3e, 0xcb, 0xf0, 0x51, 0xd7, 0x84, 0xae, 0x44, 0xbc, 0x08, 0x93, 0xae, 0x04, 0x3f, 0xe8, + 0xbb, 0xb0, 0x6e, 0x52, 0x7b, 0xec, 0x3e, 0x9c, 0x84, 0xd6, 0x32, 0xd4, 0xb9, 0x5d, 0x09, 0x4a, + 0xfc, 0xe7, 0xdd, 0x1f, 0x7f, 0x0d, 0x16, 0xf7, 0x62, 0x91, 0xea, 0xc0, 0x22, 0x9e, 0x84, 0xbc, + 0x0f, 0x33, 0x72, 0x29, 0x91, 0xf5, 0xd2, 0xbb, 0x70, 0xe4, 0xd1, 0xde, 0x18, 0xf1, 0x5e, 0x9c, + 0xae, 0xfc, 0xe8, 0x1f, 0xfe, 0xe5, 0x27, 0xb5, 0x05, 0x32, 0x77, 0xe7, 0xe4, 0xb5, 0x3b, 0x47, + 0x2c, 0xc3, 0x14, 0xc4, 0x11, 0x2c, 0x58, 0xaf, 0x7a, 0xc9, 0xa6, 0xf5, 0x32, 0xb7, 0xf0, 0xd8, + 0xb7, 0x7d, 0x75, 0xec, 0xbb, 0x5d, 0x7a, 0x19, 0x59, 0xac, 0x90, 0x4b, 0x92, 0x45, 0xfe, 0x60, + 0x97, 0x1c, 0xc3, 0x92, 0x30, 0x76, 0x4d, 0x94, 0x6c, 0xe5, 0xc4, 0x2a, 0x1f, 0x24, 0x9b, 0x43, + 0xb1, 0x76, 0x09, 0x7a, 0x05, 0xf9, 0xac, 0x91, 0x15, 0xce, 0x47, 0xac, 0x03, 0xcd, 0x8a, 0x7c, + 0x17, 0x96, 0xe5, 0xa3, 0xc8, 0x67, 0xc1, 0x6a, 0x13, 0x59, 0xad, 0x93, 0x55, 0xce, 0xca, 0x17, + 0x74, 0x73, 0x5e, 0x31, 0xd6, 0xf0, 0x98, 0x8f, 0x73, 0xc9, 0xb5, 0x91, 0xaf, 0x76, 0x05, 0xa7, + 0xad, 0x73, 0x5e, 0xf5, 0xda, 0x83, 0x3b, 0x62, 0x1c, 0x57, 0x3f, 0xec, 0x25, 0x3f, 0x11, 0xc9, + 0x95, 0xca, 0xa7, 0xe2, 0xe4, 0x85, 0xf3, 0xdf, 0xa7, 0x0b, 0x19, 0x6e, 0x4d, 0xfa, 0x90, 0x9d, + 0x7e, 0x0e, 0x85, 0xb9, 0x46, 0x36, 0xa5, 0x30, 0xd6, 0xe3, 0x75, 0xf5, 0x3c, 0x9e, 0xf4, 0x60, + 0xde, 0x7c, 0xb0, 0x4b, 0xae, 0x54, 0xe4, 0x72, 0x34, 0xf3, 0xcd, 0xea, 0x46, 0xc9, 0xb0, 0x85, + 0x0c, 0x09, 0x59, 0x96, 0x0c, 0x75, 0x01, 0x2e, 0x89, 0x60, 0xa9, 0xf0, 0xd8, 0x95, 0xd0, 0xc2, + 0xac, 0x55, 0xbc, 0x4c, 0x1e, 0x3d, 0xb3, 0xd7, 0x90, 0x53, 0x8b, 0xae, 0x18, 0x33, 0xab, 0xb8, + 0xbd, 0xed, 0xbc, 0x48, 0x52, 0x9c, 0x5b, 0xf3, 0x2d, 0xe6, 0x44, 0xfc, 0xb6, 0xce, 0x79, 0xc8, + 0x59, 0x9a, 0x5f, 0xc5, 0x13, 0xd7, 0x63, 0x8a, 0xef, 0xdb, 0x8c, 0x17, 0xc4, 0xbb, 0xb1, 0x3f, + 0xd9, 0x38, 0xaf, 0x56, 0xbf, 0x40, 0x96, 0x8f, 0xa0, 0x69, 0x1b, 0xb9, 0xae, 0x12, 0x52, 0xe0, + 0x1a, 0x67, 0x03, 0x92, 0x5a, 0x0f, 0xb4, 0x25, 0x53, 0xdb, 0x92, 0x2b, 0x9e, 0x48, 0x57, 0x8e, + 0xd4, 0x7c, 0xf3, 0x3c, 0x72, 0xa4, 0x71, 0x36, 0x48, 0x49, 0x08, 0x8b, 0xc2, 0x21, 0x3c, 0x9b, + 0xd9, 0xbc, 0x8a, 0xbc, 0x36, 0x28, 0xc9, 0x5d, 0x82, 0x39, 0x99, 0x1f, 0x42, 0x53, 0xe7, 0x96, + 0x48, 0xcb, 0x10, 0xdc, 0x7a, 0xa1, 0xda, 0x1e, 0xf1, 0xfe, 0x50, 0x59, 0x25, 0x5d, 0x90, 0x23, + 0x11, 0xaf, 0x09, 0x39, 0xe1, 0xef, 0x00, 0xe4, 0x0f, 0x12, 0xc9, 0xe5, 0x12, 0x65, 0xad, 0xad, + 0x76, 0x55, 0x93, 0x24, 0xbf, 0x8e, 0xe4, 0x97, 0xc9, 0xa2, 0x45, 0x5e, 0xad, 0x2b, 0x9d, 0x4a, + 0xb3, 0xd6, 0x55, 0xf1, 0x09, 0x63, 0x7b, 0xf4, 0xdb, 0x35, 0x35, 0x11, 0x54, 0x2d, 0x2a, 0x5d, + 0xe3, 0xc1, 0x47, 0x20, 0xb6, 0x00, 0xe3, 0xd1, 0xdc, 0x66, 0x15, 0x97, 0xca, 0x2d, 0xa0, 0xfc, + 0x02, 0xae, 0xb4, 0x05, 0xe4, 0x0f, 0xdd, 0xc8, 0x13, 0xfc, 0xfb, 0x31, 0xc6, 0x9b, 0x2f, 0x62, + 0xd2, 0x2a, 0x3f, 0x80, 0x6b, 0x5f, 0x1b, 0xd5, 0x9c, 0x56, 0xdb, 0xb4, 0xbc, 0x62, 0xc0, 0x85, + 0x74, 0x26, 0xd2, 0x71, 0x79, 0x2f, 0x91, 0xca, 0xfb, 0x55, 0x59, 0x5e, 0x47, 0x96, 0x6d, 0xd2, + 0x2a, 0xb3, 0x4c, 0x91, 0xc1, 0xab, 0x8e, 0xb4, 0x35, 0xf1, 0xc8, 0xcc, 0xb2, 0x35, 0xeb, 0x2d, + 0x5a, 0xfb, 0x72, 0x45, 0x8b, 0xe4, 0xb2, 0x86, 0x5c, 0x96, 0xc8, 0x82, 0xf6, 0xba, 0x48, 0x4b, + 0x98, 0x83, 0x7e, 0x42, 0x60, 0x99, 0x43, 0xf1, 0x89, 0x98, 0xe5, 0x66, 0x4b, 0x0f, 0xc5, 0x4a, + 0x6e, 0x56, 0x3f, 0x05, 0x23, 0x3f, 0xb0, 0x5f, 0x9c, 0xa9, 0x17, 0x30, 0x74, 0xec, 0x93, 0x15, + 0xc1, 0xf2, 0xb9, 0x09, 0x9e, 0xb5, 0xd0, 0x2d, 0xe4, 0x7c, 0x99, 0x6c, 0x14, 0x39, 0xcb, 0x27, + 0x32, 0xe4, 0x04, 0x56, 0x2a, 0x1e, 0x84, 0xe4, 0x02, 0x8c, 0x7e, 0x2d, 0x32, 0xda, 0x3b, 0x50, + 0x64, 0xba, 0x49, 0x91, 0xa9, 0xe7, 0xfb, 0x9a, 0xa9, 0x8c, 0xd5, 0xf9, 0x3a, 0xf8, 0x01, 0xac, + 0x57, 0xbf, 0xd1, 0x20, 0xcf, 0xeb, 0x3f, 0x8d, 0x31, 0xee, 0x0d, 0xc7, 0x68, 0xee, 0xcf, 0x23, + 0xf7, 0x2d, 0xda, 0xe6, 0xdc, 0x13, 0xa4, 0x51, 0x25, 0xc0, 0x29, 0x16, 0x5a, 0xd9, 0xcf, 0x13, + 0xc8, 0x75, 0x43, 0xa7, 0x95, 0xaf, 0x38, 0xda, 0x37, 0xc6, 0x60, 0xd8, 0xce, 0x91, 0xac, 0x49, + 0x9d, 0x63, 0x4d, 0xbf, 0x7e, 0xe7, 0x20, 0x3d, 0x40, 0x5e, 0xfe, 0x6f, 0x79, 0x80, 0xd2, 0x8b, + 0x06, 0xcb, 0x03, 0x94, 0x1f, 0x19, 0x94, 0x3c, 0x00, 0x32, 0xc3, 0x07, 0x07, 0xe4, 0x23, 0x5c, + 0x19, 0xb2, 0xca, 0xaf, 0x55, 0x74, 0x24, 0x69, 0xd5, 0xca, 0xb0, 0xeb, 0xf8, 0x4a, 0x8e, 0x58, + 0x14, 0x0f, 0x72, 0xed, 0xb9, 0x30, 0xab, 0xd0, 0xc9, 0x46, 0x91, 0x80, 0xa2, 0x5c, 0x59, 0x91, + 0x4d, 0x37, 0x90, 0xe8, 0x25, 0x3a, 0x6f, 0x12, 0xe5, 0x34, 0xbb, 0x30, 0x67, 0x54, 0x1f, 0x13, + 0xed, 0xc2, 0xcb, 0xc5, 0xd6, 0xed, 0x2b, 0x95, 0x6d, 0xb6, 0xa3, 0xa2, 0x4b, 0x9c, 0x41, 0x8a, + 0x08, 0x9a, 0xc7, 0x77, 0x61, 0xc1, 0x2a, 0x00, 0xce, 0x95, 0x5f, 0x55, 0xa2, 0x9c, 0x2b, 0xbf, + 0xb2, 0x6a, 0x58, 0x85, 0xab, 0x14, 0x95, 0x9f, 0x4a, 0x14, 0xcd, 0xeb, 0x63, 0x68, 0xea, 0xba, + 0xdb, 0x5c, 0xff, 0xc5, 0x52, 0xdc, 0xf3, 0x78, 0x58, 0x73, 0x70, 0xca, 0x3b, 0x77, 0xe3, 0x7e, + 0x57, 0xd0, 0x9f, 0x33, 0xaa, 0x68, 0x73, 0x7d, 0x95, 0x4b, 0x6b, 0x47, 0x2f, 0x16, 0x4b, 0x57, + 0x3d, 0xec, 0xa8, 0xe5, 0x4f, 0x60, 0xa9, 0x50, 0xe0, 0x99, 0x07, 0x29, 0xd5, 0xe5, 0xac, 0x79, + 0x90, 0x32, 0xa2, 0x32, 0xd4, 0x0e, 0x03, 0x05, 0x3f, 0x2f, 0x0c, 0x73, 0xbb, 0x12, 0xde, 0x5c, + 0x54, 0x76, 0x58, 0x36, 0x6b, 0xd5, 0x79, 0x5a, 0x36, 0x6b, 0xd7, 0x4a, 0x96, 0xbc, 0xb9, 0xb8, + 0x50, 0x21, 0x1f, 0xc0, 0xac, 0xaa, 0xbb, 0xcb, 0x0d, 0xb6, 0x50, 0x71, 0xd8, 0x6e, 0x95, 0x1b, + 0x24, 0x55, 0xcb, 0x68, 0x3d, 0xdf, 0x47, 0xaa, 0x72, 0x12, 0x8c, 0x5a, 0xbd, 0x7c, 0x12, 0xca, + 0x05, 0x7c, 0x13, 0x4e, 0x82, 0xf0, 0x58, 0x9a, 0xfe, 0x5f, 0x38, 0x78, 0xd1, 0x37, 0xbe, 0xae, + 0x8e, 0xbc, 0x7a, 0x81, 0x12, 0x3c, 0x21, 0xcc, 0x6b, 0x17, 0x2e, 0xda, 0xa3, 0xb7, 0x50, 0x4c, + 0x4a, 0xaf, 0xaa, 0x7d, 0x12, 0xbb, 0xf9, 0x02, 0x5d, 0x57, 0xf0, 0x71, 0xa1, 0xff, 0xdc, 0x11, + 0x7f, 0x73, 0x6c, 0x0c, 0x5d, 0xb2, 0x3d, 0xa1, 0x00, 0x4a, 0xe0, 0x3b, 0x13, 0xe3, 0x4b, 0x71, + 0x6f, 0xa2, 0xb8, 0xd7, 0xe9, 0x95, 0x31, 0xe2, 0x72, 0x61, 0x43, 0xb8, 0x64, 0xd6, 0xdf, 0xbd, + 0x3b, 0x8c, 0x7c, 0xe3, 0x4c, 0x55, 0x51, 0x9a, 0x97, 0x9b, 0x49, 0xb1, 0x5a, 0x4c, 0x05, 0x2c, + 0x14, 0x5d, 0xff, 0xa9, 0x6c, 0x3d, 0x0c, 0xbc, 0xec, 0x90, 0x53, 0xe5, 0xdc, 0x7e, 0xec, 0xe4, + 0xa5, 0x5f, 0xf6, 0x30, 0x04, 0xe3, 0xab, 0x45, 0xda, 0x56, 0x85, 0xdd, 0x18, 0xd6, 0xaf, 0x23, + 0xeb, 0x57, 0xe8, 0x2d, 0x93, 0xb5, 0xfc, 0x4f, 0x0c, 0x1d, 0x65, 0xb0, 0xa5, 0xf9, 0x91, 0x51, + 0x7c, 0x68, 0x14, 0xa2, 0xe5, 0xdb, 0xff, 0xe8, 0x9a, 0xb6, 0x3c, 0xfe, 0x18, 0x53, 0xc9, 0x66, + 0x87, 0x02, 0xa7, 0x1a, 0x11, 0xcd, 0xbb, 0x7b, 0x16, 0xf8, 0x5c, 0x88, 0x9f, 0x39, 0xa5, 0xda, + 0x39, 0xa3, 0xaa, 0x8b, 0xdc, 0x1e, 0xc1, 0xa7, 0x5c, 0xdb, 0xd6, 0x7e, 0x71, 0x12, 0xd4, 0x0b, + 0x48, 0xf6, 0x87, 0x56, 0x8d, 0x92, 0x59, 0xea, 0x96, 0x47, 0x29, 0x63, 0x4b, 0xe1, 0x2e, 0x24, + 0x91, 0x3c, 0xfd, 0xd3, 0xcb, 0x95, 0x12, 0xf9, 0x5e, 0x26, 0x0f, 0xca, 0xcb, 0xc5, 0xb2, 0x17, + 0x33, 0xe1, 0x52, 0x59, 0xa0, 0xd2, 0xbe, 0x3e, 0x1a, 0xa1, 0x2a, 0xf3, 0x72, 0xc4, 0x32, 0x51, + 0xc1, 0xe2, 0x4b, 0x06, 0x27, 0xb0, 0xbc, 0x3f, 0x92, 0xe9, 0xfe, 0xa7, 0x66, 0x2a, 0xa3, 0x53, + 0x8a, 0x4c, 0xd3, 0x02, 0x53, 0x3e, 0xd8, 0x13, 0x51, 0xe3, 0x6f, 0x16, 0xa8, 0x90, 0xad, 0xd1, + 0xa5, 0x2b, 0x65, 0xbe, 0x95, 0xb5, 0x2d, 0x36, 0x5f, 0xe3, 0xa8, 0x8c, 0x55, 0x1f, 0x22, 0x4c, + 0x58, 0x2a, 0x54, 0x9e, 0xe4, 0x5b, 0x5f, 0x75, 0x49, 0xca, 0x84, 0x99, 0x8f, 0xd4, 0x66, 0xc6, + 0x79, 0x65, 0x98, 0x84, 0x28, 0x54, 0x70, 0x90, 0x1b, 0x55, 0x07, 0x3f, 0xab, 0x40, 0x62, 0xdc, + 0x11, 0x54, 0xf2, 0x24, 0xeb, 0xa5, 0x73, 0xa1, 0x3a, 0x36, 0xfd, 0x9e, 0xb8, 0x71, 0x1f, 0x51, + 0x40, 0x42, 0x6e, 0x57, 0x65, 0x1b, 0x2e, 0x2c, 0x86, 0x74, 0xc1, 0xe4, 0x5a, 0x31, 0x25, 0x51, + 0x12, 0xe7, 0x18, 0xd3, 0x3f, 0x66, 0x19, 0x88, 0x95, 0x10, 0xa9, 0xa8, 0x0f, 0x19, 0x99, 0x3d, + 0x28, 0xe6, 0x41, 0xe4, 0xf1, 0x5e, 0x71, 0xfa, 0xa1, 0xfd, 0xa7, 0xe6, 0x2c, 0x96, 0x37, 0x2b, + 0x46, 0x7d, 0x11, 0xd6, 0xcf, 0x21, 0xeb, 0xab, 0xe4, 0x4a, 0x61, 0xbc, 0x05, 0x11, 0xc4, 0x09, + 0xc0, 0xb8, 0x98, 0x37, 0x4f, 0x00, 0xa5, 0x9a, 0x16, 0xeb, 0x04, 0x50, 0x2e, 0x2b, 0x29, 0x9d, + 0x00, 0x3c, 0x8e, 0x82, 0x4e, 0x83, 0x3c, 0x81, 0xe5, 0xe2, 0x05, 0xb9, 0xb1, 0x7c, 0xaa, 0xaf, + 0xce, 0xcf, 0x4d, 0xfa, 0xc8, 0x73, 0x4d, 0x2f, 0x13, 0x29, 0xfd, 0x3b, 0xf2, 0x0d, 0x07, 0x79, + 0x02, 0x4b, 0x85, 0x3b, 0x6b, 0x63, 0x0a, 0x2b, 0x2f, 0xb3, 0x47, 0xb3, 0xb2, 0x17, 0xa8, 0x66, + 0x35, 0xc4, 0xde, 0x7c, 0xd1, 0x3c, 0x85, 0x95, 0x8a, 0x6b, 0x67, 0xe3, 0xdc, 0x3c, 0xf2, 0x4e, + 0xba, 0x5d, 0x16, 0xca, 0xba, 0x7e, 0xb5, 0x73, 0x5b, 0x39, 0xef, 0x84, 0x09, 0xce, 0x03, 0x63, + 0x98, 0xf2, 0x6f, 0xc9, 0x96, 0x29, 0x5a, 0x37, 0xfd, 0xed, 0xad, 0x91, 0xed, 0x95, 0xce, 0x57, + 0xb3, 0x94, 0xb7, 0xb1, 0x21, 0x2c, 0xda, 0xa2, 0x1a, 0x69, 0x95, 0xaa, 0x1b, 0xf3, 0x73, 0x47, + 0x68, 0xaf, 0x10, 0xcd, 0xee, 0x13, 0xa4, 0xcd, 0x60, 0xc1, 0xaa, 0x65, 0x30, 0x8c, 0xb3, 0xa2, + 0x4a, 0x62, 0xc2, 0x14, 0xa1, 0x39, 0xa6, 0x78, 0xc0, 0xd5, 0x68, 0x9a, 0xa6, 0x2c, 0x99, 0x20, + 0x5b, 0x95, 0x9c, 0xf2, 0xba, 0x88, 0x4f, 0xcd, 0x2c, 0x35, 0x98, 0xc9, 0x52, 0x8b, 0x0a, 0x66, + 0x76, 0x11, 0xc6, 0xf9, 0xb3, 0x76, 0x0e, 0xd3, 0x53, 0xa3, 0x80, 0x40, 0x15, 0x23, 0x1c, 0xc4, + 0x47, 0x47, 0x21, 0x33, 0xd2, 0x0c, 0x23, 0xaa, 0x15, 0x46, 0x8f, 0xf4, 0x06, 0x32, 0xbd, 0x42, + 0xd7, 0x6d, 0xa6, 0xde, 0x30, 0x8b, 0xd5, 0xda, 0xf8, 0x3e, 0x6e, 0x28, 0x85, 0x5a, 0x2a, 0x6b, + 0x43, 0xa9, 0x2e, 0x2b, 0x6b, 0xd3, 0x71, 0x28, 0x23, 0x76, 0x96, 0x63, 0x89, 0xd7, 0x93, 0x6c, + 0xbe, 0x8b, 0x91, 0x82, 0x75, 0xe9, 0x6d, 0x45, 0x0a, 0x55, 0x05, 0x04, 0x13, 0xde, 0x07, 0x19, + 0x7b, 0xa7, 0xb8, 0xe7, 0x4c, 0x61, 0x65, 0x9f, 0xf1, 0x29, 0xb3, 0x03, 0x04, 0x5a, 0xc5, 0xce, + 0x2e, 0x25, 0x38, 0xd7, 0xf3, 0x88, 0x84, 0x59, 0xca, 0x32, 0x2f, 0x0c, 0xad, 0xe8, 0x80, 0xfc, + 0xbe, 0x03, 0x9b, 0xe3, 0x2a, 0x0a, 0xc8, 0x4b, 0x8a, 0xf4, 0x04, 0x75, 0x07, 0xa3, 0xe5, 0x90, + 0x87, 0x2d, 0x72, 0x9d, 0xcb, 0x21, 0x6a, 0xf1, 0xf5, 0xe5, 0x8c, 0xa2, 0x24, 0x04, 0x12, 0x89, + 0x2c, 0xbb, 0xcc, 0x80, 0x54, 0xc5, 0x40, 0x56, 0xe5, 0x82, 0x95, 0xc8, 0xaa, 0xae, 0x51, 0x28, + 0x25, 0xb2, 0x2c, 0xed, 0xa7, 0x7c, 0x55, 0x15, 0x4b, 0x0e, 0xf2, 0xa9, 0x1e, 0x51, 0xc4, 0x90, + 0x07, 0x67, 0xa3, 0xaa, 0x15, 0xec, 0x39, 0x3f, 0x55, 0x58, 0xea, 0x0a, 0x35, 0x85, 0x95, 0x8a, + 0x2b, 0x7d, 0xe3, 0xc0, 0x32, 0xf2, 0xbe, 0x7f, 0xc2, 0x39, 0xd7, 0x1c, 0xb9, 0xb9, 0x49, 0xea, + 0x3f, 0x73, 0xe0, 0xf2, 0xc8, 0x8b, 0x73, 0x72, 0xab, 0x6a, 0x48, 0x55, 0x95, 0x01, 0xed, 0xdb, + 0x13, 0x60, 0xda, 0x59, 0x4c, 0x72, 0xb5, 0xa8, 0x05, 0xeb, 0x2e, 0x9d, 0xf4, 0xe1, 0x52, 0xe9, + 0x2e, 0x9d, 0x5c, 0xaf, 0x52, 0x86, 0x79, 0xcd, 0x3e, 0xe1, 0x1e, 0x6f, 0xaa, 0x02, 0x2f, 0xdb, + 0xc9, 0x11, 0x2c, 0x15, 0x2e, 0xdb, 0xf3, 0xcd, 0xaf, 0xfa, 0x16, 0x7e, 0xc2, 0x6b, 0x65, 0x93, + 0xd5, 0x30, 0x09, 0xbb, 0xd3, 0xf8, 0x24, 0xe5, 0xf5, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xfa, + 0x35, 0xc5, 0x70, 0x58, 0x5f, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -10951,16 +7634,16 @@ const _ = grpc.SupportPackageIsVersion6 type GoCryptoTraderClient interface { GetInfo(ctx context.Context, in *GetInfoRequest, opts ...grpc.CallOption) (*GetInfoResponse, error) GetSubsystems(ctx context.Context, in *GetSubsystemsRequest, opts ...grpc.CallOption) (*GetSusbsytemsResponse, error) - EnableSubsystem(ctx context.Context, in *GenericSubsystemRequest, opts ...grpc.CallOption) (*GenericSubsystemResponse, error) - DisableSubsystem(ctx context.Context, in *GenericSubsystemRequest, opts ...grpc.CallOption) (*GenericSubsystemResponse, error) + EnableSubsystem(ctx context.Context, in *GenericSubsystemRequest, opts ...grpc.CallOption) (*GenericResponse, error) + DisableSubsystem(ctx context.Context, in *GenericSubsystemRequest, opts ...grpc.CallOption) (*GenericResponse, error) GetRPCEndpoints(ctx context.Context, in *GetRPCEndpointsRequest, opts ...grpc.CallOption) (*GetRPCEndpointsResponse, error) GetCommunicationRelayers(ctx context.Context, in *GetCommunicationRelayersRequest, opts ...grpc.CallOption) (*GetCommunicationRelayersResponse, error) GetExchanges(ctx context.Context, in *GetExchangesRequest, opts ...grpc.CallOption) (*GetExchangesResponse, error) - DisableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) + DisableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericResponse, error) GetExchangeInfo(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GetExchangeInfoResponse, error) GetExchangeOTPCode(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GetExchangeOTPReponse, error) GetExchangeOTPCodes(ctx context.Context, in *GetExchangeOTPsRequest, opts ...grpc.CallOption) (*GetExchangeOTPsResponse, error) - EnableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) + EnableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericResponse, error) GetTicker(ctx context.Context, in *GetTickerRequest, opts ...grpc.CallOption) (*TickerResponse, error) GetTickers(ctx context.Context, in *GetTickersRequest, opts ...grpc.CallOption) (*GetTickersResponse, error) GetOrderbook(ctx context.Context, in *GetOrderbookRequest, opts ...grpc.CallOption) (*OrderbookResponse, error) @@ -10970,8 +7653,8 @@ type GoCryptoTraderClient interface { GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error) GetPortfolio(ctx context.Context, in *GetPortfolioRequest, opts ...grpc.CallOption) (*GetPortfolioResponse, error) GetPortfolioSummary(ctx context.Context, in *GetPortfolioSummaryRequest, opts ...grpc.CallOption) (*GetPortfolioSummaryResponse, error) - AddPortfolioAddress(ctx context.Context, in *AddPortfolioAddressRequest, opts ...grpc.CallOption) (*AddPortfolioAddressResponse, error) - RemovePortfolioAddress(ctx context.Context, in *RemovePortfolioAddressRequest, opts ...grpc.CallOption) (*RemovePortfolioAddressResponse, error) + AddPortfolioAddress(ctx context.Context, in *AddPortfolioAddressRequest, opts ...grpc.CallOption) (*GenericResponse, error) + RemovePortfolioAddress(ctx context.Context, in *RemovePortfolioAddressRequest, opts ...grpc.CallOption) (*GenericResponse, error) GetForexProviders(ctx context.Context, in *GetForexProvidersRequest, opts ...grpc.CallOption) (*GetForexProvidersResponse, error) GetForexRates(ctx context.Context, in *GetForexRatesRequest, opts ...grpc.CallOption) (*GetForexRatesResponse, error) GetOrders(ctx context.Context, in *GetOrdersRequest, opts ...grpc.CallOption) (*GetOrdersResponse, error) @@ -10979,11 +7662,11 @@ type GoCryptoTraderClient interface { SubmitOrder(ctx context.Context, in *SubmitOrderRequest, opts ...grpc.CallOption) (*SubmitOrderResponse, error) SimulateOrder(ctx context.Context, in *SimulateOrderRequest, opts ...grpc.CallOption) (*SimulateOrderResponse, error) WhaleBomb(ctx context.Context, in *WhaleBombRequest, opts ...grpc.CallOption) (*SimulateOrderResponse, error) - CancelOrder(ctx context.Context, in *CancelOrderRequest, opts ...grpc.CallOption) (*CancelOrderResponse, error) + CancelOrder(ctx context.Context, in *CancelOrderRequest, opts ...grpc.CallOption) (*GenericResponse, error) CancelAllOrders(ctx context.Context, in *CancelAllOrdersRequest, opts ...grpc.CallOption) (*CancelAllOrdersResponse, error) GetEvents(ctx context.Context, in *GetEventsRequest, opts ...grpc.CallOption) (*GetEventsResponse, error) AddEvent(ctx context.Context, in *AddEventRequest, opts ...grpc.CallOption) (*AddEventResponse, error) - RemoveEvent(ctx context.Context, in *RemoveEventRequest, opts ...grpc.CallOption) (*RemoveEventResponse, error) + RemoveEvent(ctx context.Context, in *RemoveEventRequest, opts ...grpc.CallOption) (*GenericResponse, error) GetCryptocurrencyDepositAddresses(ctx context.Context, in *GetCryptocurrencyDepositAddressesRequest, opts ...grpc.CallOption) (*GetCryptocurrencyDepositAddressesResponse, error) GetCryptocurrencyDepositAddress(ctx context.Context, in *GetCryptocurrencyDepositAddressRequest, opts ...grpc.CallOption) (*GetCryptocurrencyDepositAddressResponse, error) WithdrawFiatFunds(ctx context.Context, in *WithdrawFiatRequest, opts ...grpc.CallOption) (*WithdrawResponse, error) @@ -10994,23 +7677,31 @@ type GoCryptoTraderClient interface { GetLoggerDetails(ctx context.Context, in *GetLoggerDetailsRequest, opts ...grpc.CallOption) (*GetLoggerDetailsResponse, error) SetLoggerDetails(ctx context.Context, in *SetLoggerDetailsRequest, opts ...grpc.CallOption) (*GetLoggerDetailsResponse, error) GetExchangePairs(ctx context.Context, in *GetExchangePairsRequest, opts ...grpc.CallOption) (*GetExchangePairsResponse, error) - EnableExchangePair(ctx context.Context, in *ExchangePairRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) - DisableExchangePair(ctx context.Context, in *ExchangePairRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) + SetExchangePair(ctx context.Context, in *SetExchangePairRequest, opts ...grpc.CallOption) (*GenericResponse, error) GetOrderbookStream(ctx context.Context, in *GetOrderbookStreamRequest, opts ...grpc.CallOption) (GoCryptoTrader_GetOrderbookStreamClient, error) GetExchangeOrderbookStream(ctx context.Context, in *GetExchangeOrderbookStreamRequest, opts ...grpc.CallOption) (GoCryptoTrader_GetExchangeOrderbookStreamClient, error) GetTickerStream(ctx context.Context, in *GetTickerStreamRequest, opts ...grpc.CallOption) (GoCryptoTrader_GetTickerStreamClient, error) GetExchangeTickerStream(ctx context.Context, in *GetExchangeTickerStreamRequest, opts ...grpc.CallOption) (GoCryptoTrader_GetExchangeTickerStreamClient, error) GetAuditEvent(ctx context.Context, in *GetAuditEventRequest, opts ...grpc.CallOption) (*GetAuditEventResponse, error) - GCTScriptExecute(ctx context.Context, in *GCTScriptExecuteRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) - GCTScriptUpload(ctx context.Context, in *GCTScriptUploadRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) + GCTScriptExecute(ctx context.Context, in *GCTScriptExecuteRequest, opts ...grpc.CallOption) (*GenericResponse, error) + GCTScriptUpload(ctx context.Context, in *GCTScriptUploadRequest, opts ...grpc.CallOption) (*GenericResponse, error) GCTScriptReadScript(ctx context.Context, in *GCTScriptReadScriptRequest, opts ...grpc.CallOption) (*GCTScriptQueryResponse, error) GCTScriptStatus(ctx context.Context, in *GCTScriptStatusRequest, opts ...grpc.CallOption) (*GCTScriptStatusResponse, error) GCTScriptQuery(ctx context.Context, in *GCTScriptQueryRequest, opts ...grpc.CallOption) (*GCTScriptQueryResponse, error) - GCTScriptStop(ctx context.Context, in *GCTScriptStopRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) - GCTScriptStopAll(ctx context.Context, in *GCTScriptStopAllRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) + GCTScriptStop(ctx context.Context, in *GCTScriptStopRequest, opts ...grpc.CallOption) (*GenericResponse, error) + GCTScriptStopAll(ctx context.Context, in *GCTScriptStopAllRequest, opts ...grpc.CallOption) (*GenericResponse, error) GCTScriptListAll(ctx context.Context, in *GCTScriptListAllRequest, opts ...grpc.CallOption) (*GCTScriptStatusResponse, error) - GCTScriptAutoLoadToggle(ctx context.Context, in *GCTScriptAutoLoadRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) + GCTScriptAutoLoadToggle(ctx context.Context, in *GCTScriptAutoLoadRequest, opts ...grpc.CallOption) (*GenericResponse, error) GetHistoricCandles(ctx context.Context, in *GetHistoricCandlesRequest, opts ...grpc.CallOption) (*GetHistoricCandlesResponse, error) + SetExchangeAsset(ctx context.Context, in *SetExchangeAssetRequest, opts ...grpc.CallOption) (*GenericResponse, error) + SetAllExchangePairs(ctx context.Context, in *SetExchangeAllPairsRequest, opts ...grpc.CallOption) (*GenericResponse, error) + UpdateExchangeSupportedPairs(ctx context.Context, in *UpdateExchangeSupportedPairsRequest, opts ...grpc.CallOption) (*GenericResponse, error) + GetExchangeAssets(ctx context.Context, in *GetExchangeAssetsRequest, opts ...grpc.CallOption) (*GetExchangeAssetsResponse, error) + WebsocketGetInfo(ctx context.Context, in *WebsocketGetInfoRequest, opts ...grpc.CallOption) (*WebsocketGetInfoResponse, error) + WebsocketSetEnabled(ctx context.Context, in *WebsocketSetEnabledRequest, opts ...grpc.CallOption) (*GenericResponse, error) + WebsocketGetSubscriptions(ctx context.Context, in *WebsocketGetSubscriptionsRequest, opts ...grpc.CallOption) (*WebsocketGetSubscriptionsResponse, error) + WebsocketSetProxy(ctx context.Context, in *WebsocketSetProxyRequest, opts ...grpc.CallOption) (*GenericResponse, error) + WebsocketSetURL(ctx context.Context, in *WebsocketSetURLRequest, opts ...grpc.CallOption) (*GenericResponse, error) } type goCryptoTraderClient struct { @@ -11039,8 +7730,8 @@ func (c *goCryptoTraderClient) GetSubsystems(ctx context.Context, in *GetSubsyst return out, nil } -func (c *goCryptoTraderClient) EnableSubsystem(ctx context.Context, in *GenericSubsystemRequest, opts ...grpc.CallOption) (*GenericSubsystemResponse, error) { - out := new(GenericSubsystemResponse) +func (c *goCryptoTraderClient) EnableSubsystem(ctx context.Context, in *GenericSubsystemRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/EnableSubsystem", in, out, opts...) if err != nil { return nil, err @@ -11048,8 +7739,8 @@ func (c *goCryptoTraderClient) EnableSubsystem(ctx context.Context, in *GenericS return out, nil } -func (c *goCryptoTraderClient) DisableSubsystem(ctx context.Context, in *GenericSubsystemRequest, opts ...grpc.CallOption) (*GenericSubsystemResponse, error) { - out := new(GenericSubsystemResponse) +func (c *goCryptoTraderClient) DisableSubsystem(ctx context.Context, in *GenericSubsystemRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/DisableSubsystem", in, out, opts...) if err != nil { return nil, err @@ -11084,8 +7775,8 @@ func (c *goCryptoTraderClient) GetExchanges(ctx context.Context, in *GetExchange return out, nil } -func (c *goCryptoTraderClient) DisableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) { - out := new(GenericExchangeNameResponse) +func (c *goCryptoTraderClient) DisableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/DisableExchange", in, out, opts...) if err != nil { return nil, err @@ -11120,8 +7811,8 @@ func (c *goCryptoTraderClient) GetExchangeOTPCodes(ctx context.Context, in *GetE return out, nil } -func (c *goCryptoTraderClient) EnableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) { - out := new(GenericExchangeNameResponse) +func (c *goCryptoTraderClient) EnableExchange(ctx context.Context, in *GenericExchangeNameRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/EnableExchange", in, out, opts...) if err != nil { return nil, err @@ -11233,8 +7924,8 @@ func (c *goCryptoTraderClient) GetPortfolioSummary(ctx context.Context, in *GetP return out, nil } -func (c *goCryptoTraderClient) AddPortfolioAddress(ctx context.Context, in *AddPortfolioAddressRequest, opts ...grpc.CallOption) (*AddPortfolioAddressResponse, error) { - out := new(AddPortfolioAddressResponse) +func (c *goCryptoTraderClient) AddPortfolioAddress(ctx context.Context, in *AddPortfolioAddressRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/AddPortfolioAddress", in, out, opts...) if err != nil { return nil, err @@ -11242,8 +7933,8 @@ func (c *goCryptoTraderClient) AddPortfolioAddress(ctx context.Context, in *AddP return out, nil } -func (c *goCryptoTraderClient) RemovePortfolioAddress(ctx context.Context, in *RemovePortfolioAddressRequest, opts ...grpc.CallOption) (*RemovePortfolioAddressResponse, error) { - out := new(RemovePortfolioAddressResponse) +func (c *goCryptoTraderClient) RemovePortfolioAddress(ctx context.Context, in *RemovePortfolioAddressRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/RemovePortfolioAddress", in, out, opts...) if err != nil { return nil, err @@ -11314,8 +8005,8 @@ func (c *goCryptoTraderClient) WhaleBomb(ctx context.Context, in *WhaleBombReque return out, nil } -func (c *goCryptoTraderClient) CancelOrder(ctx context.Context, in *CancelOrderRequest, opts ...grpc.CallOption) (*CancelOrderResponse, error) { - out := new(CancelOrderResponse) +func (c *goCryptoTraderClient) CancelOrder(ctx context.Context, in *CancelOrderRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/CancelOrder", in, out, opts...) if err != nil { return nil, err @@ -11350,8 +8041,8 @@ func (c *goCryptoTraderClient) AddEvent(ctx context.Context, in *AddEventRequest return out, nil } -func (c *goCryptoTraderClient) RemoveEvent(ctx context.Context, in *RemoveEventRequest, opts ...grpc.CallOption) (*RemoveEventResponse, error) { - out := new(RemoveEventResponse) +func (c *goCryptoTraderClient) RemoveEvent(ctx context.Context, in *RemoveEventRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/RemoveEvent", in, out, opts...) if err != nil { return nil, err @@ -11449,18 +8140,9 @@ func (c *goCryptoTraderClient) GetExchangePairs(ctx context.Context, in *GetExch return out, nil } -func (c *goCryptoTraderClient) EnableExchangePair(ctx context.Context, in *ExchangePairRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) { - out := new(GenericExchangeNameResponse) - err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/EnableExchangePair", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *goCryptoTraderClient) DisableExchangePair(ctx context.Context, in *ExchangePairRequest, opts ...grpc.CallOption) (*GenericExchangeNameResponse, error) { - out := new(GenericExchangeNameResponse) - err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/DisableExchangePair", in, out, opts...) +func (c *goCryptoTraderClient) SetExchangePair(ctx context.Context, in *SetExchangePairRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/SetExchangePair", in, out, opts...) if err != nil { return nil, err } @@ -11604,8 +8286,8 @@ func (c *goCryptoTraderClient) GetAuditEvent(ctx context.Context, in *GetAuditEv return out, nil } -func (c *goCryptoTraderClient) GCTScriptExecute(ctx context.Context, in *GCTScriptExecuteRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) { - out := new(GCTScriptGenericResponse) +func (c *goCryptoTraderClient) GCTScriptExecute(ctx context.Context, in *GCTScriptExecuteRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GCTScriptExecute", in, out, opts...) if err != nil { return nil, err @@ -11613,8 +8295,8 @@ func (c *goCryptoTraderClient) GCTScriptExecute(ctx context.Context, in *GCTScri return out, nil } -func (c *goCryptoTraderClient) GCTScriptUpload(ctx context.Context, in *GCTScriptUploadRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) { - out := new(GCTScriptGenericResponse) +func (c *goCryptoTraderClient) GCTScriptUpload(ctx context.Context, in *GCTScriptUploadRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GCTScriptUpload", in, out, opts...) if err != nil { return nil, err @@ -11649,8 +8331,8 @@ func (c *goCryptoTraderClient) GCTScriptQuery(ctx context.Context, in *GCTScript return out, nil } -func (c *goCryptoTraderClient) GCTScriptStop(ctx context.Context, in *GCTScriptStopRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) { - out := new(GCTScriptGenericResponse) +func (c *goCryptoTraderClient) GCTScriptStop(ctx context.Context, in *GCTScriptStopRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GCTScriptStop", in, out, opts...) if err != nil { return nil, err @@ -11658,8 +8340,8 @@ func (c *goCryptoTraderClient) GCTScriptStop(ctx context.Context, in *GCTScriptS return out, nil } -func (c *goCryptoTraderClient) GCTScriptStopAll(ctx context.Context, in *GCTScriptStopAllRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) { - out := new(GCTScriptGenericResponse) +func (c *goCryptoTraderClient) GCTScriptStopAll(ctx context.Context, in *GCTScriptStopAllRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GCTScriptStopAll", in, out, opts...) if err != nil { return nil, err @@ -11676,8 +8358,8 @@ func (c *goCryptoTraderClient) GCTScriptListAll(ctx context.Context, in *GCTScri return out, nil } -func (c *goCryptoTraderClient) GCTScriptAutoLoadToggle(ctx context.Context, in *GCTScriptAutoLoadRequest, opts ...grpc.CallOption) (*GCTScriptGenericResponse, error) { - out := new(GCTScriptGenericResponse) +func (c *goCryptoTraderClient) GCTScriptAutoLoadToggle(ctx context.Context, in *GCTScriptAutoLoadRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GCTScriptAutoLoadToggle", in, out, opts...) if err != nil { return nil, err @@ -11694,20 +8376,101 @@ func (c *goCryptoTraderClient) GetHistoricCandles(ctx context.Context, in *GetHi return out, nil } +func (c *goCryptoTraderClient) SetExchangeAsset(ctx context.Context, in *SetExchangeAssetRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/SetExchangeAsset", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) SetAllExchangePairs(ctx context.Context, in *SetExchangeAllPairsRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/SetAllExchangePairs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) UpdateExchangeSupportedPairs(ctx context.Context, in *UpdateExchangeSupportedPairsRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/UpdateExchangeSupportedPairs", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) GetExchangeAssets(ctx context.Context, in *GetExchangeAssetsRequest, opts ...grpc.CallOption) (*GetExchangeAssetsResponse, error) { + out := new(GetExchangeAssetsResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/GetExchangeAssets", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) WebsocketGetInfo(ctx context.Context, in *WebsocketGetInfoRequest, opts ...grpc.CallOption) (*WebsocketGetInfoResponse, error) { + out := new(WebsocketGetInfoResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/WebsocketGetInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) WebsocketSetEnabled(ctx context.Context, in *WebsocketSetEnabledRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/WebsocketSetEnabled", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) WebsocketGetSubscriptions(ctx context.Context, in *WebsocketGetSubscriptionsRequest, opts ...grpc.CallOption) (*WebsocketGetSubscriptionsResponse, error) { + out := new(WebsocketGetSubscriptionsResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/WebsocketGetSubscriptions", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) WebsocketSetProxy(ctx context.Context, in *WebsocketSetProxyRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/WebsocketSetProxy", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *goCryptoTraderClient) WebsocketSetURL(ctx context.Context, in *WebsocketSetURLRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + out := new(GenericResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTrader/WebsocketSetURL", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GoCryptoTraderServer is the server API for GoCryptoTrader service. type GoCryptoTraderServer interface { GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) GetSubsystems(context.Context, *GetSubsystemsRequest) (*GetSusbsytemsResponse, error) - EnableSubsystem(context.Context, *GenericSubsystemRequest) (*GenericSubsystemResponse, error) - DisableSubsystem(context.Context, *GenericSubsystemRequest) (*GenericSubsystemResponse, error) + EnableSubsystem(context.Context, *GenericSubsystemRequest) (*GenericResponse, error) + DisableSubsystem(context.Context, *GenericSubsystemRequest) (*GenericResponse, error) GetRPCEndpoints(context.Context, *GetRPCEndpointsRequest) (*GetRPCEndpointsResponse, error) GetCommunicationRelayers(context.Context, *GetCommunicationRelayersRequest) (*GetCommunicationRelayersResponse, error) GetExchanges(context.Context, *GetExchangesRequest) (*GetExchangesResponse, error) - DisableExchange(context.Context, *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) + DisableExchange(context.Context, *GenericExchangeNameRequest) (*GenericResponse, error) GetExchangeInfo(context.Context, *GenericExchangeNameRequest) (*GetExchangeInfoResponse, error) GetExchangeOTPCode(context.Context, *GenericExchangeNameRequest) (*GetExchangeOTPReponse, error) GetExchangeOTPCodes(context.Context, *GetExchangeOTPsRequest) (*GetExchangeOTPsResponse, error) - EnableExchange(context.Context, *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) + EnableExchange(context.Context, *GenericExchangeNameRequest) (*GenericResponse, error) GetTicker(context.Context, *GetTickerRequest) (*TickerResponse, error) GetTickers(context.Context, *GetTickersRequest) (*GetTickersResponse, error) GetOrderbook(context.Context, *GetOrderbookRequest) (*OrderbookResponse, error) @@ -11717,8 +8480,8 @@ type GoCryptoTraderServer interface { GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) GetPortfolio(context.Context, *GetPortfolioRequest) (*GetPortfolioResponse, error) GetPortfolioSummary(context.Context, *GetPortfolioSummaryRequest) (*GetPortfolioSummaryResponse, error) - AddPortfolioAddress(context.Context, *AddPortfolioAddressRequest) (*AddPortfolioAddressResponse, error) - RemovePortfolioAddress(context.Context, *RemovePortfolioAddressRequest) (*RemovePortfolioAddressResponse, error) + AddPortfolioAddress(context.Context, *AddPortfolioAddressRequest) (*GenericResponse, error) + RemovePortfolioAddress(context.Context, *RemovePortfolioAddressRequest) (*GenericResponse, error) GetForexProviders(context.Context, *GetForexProvidersRequest) (*GetForexProvidersResponse, error) GetForexRates(context.Context, *GetForexRatesRequest) (*GetForexRatesResponse, error) GetOrders(context.Context, *GetOrdersRequest) (*GetOrdersResponse, error) @@ -11726,11 +8489,11 @@ type GoCryptoTraderServer interface { SubmitOrder(context.Context, *SubmitOrderRequest) (*SubmitOrderResponse, error) SimulateOrder(context.Context, *SimulateOrderRequest) (*SimulateOrderResponse, error) WhaleBomb(context.Context, *WhaleBombRequest) (*SimulateOrderResponse, error) - CancelOrder(context.Context, *CancelOrderRequest) (*CancelOrderResponse, error) + CancelOrder(context.Context, *CancelOrderRequest) (*GenericResponse, error) CancelAllOrders(context.Context, *CancelAllOrdersRequest) (*CancelAllOrdersResponse, error) GetEvents(context.Context, *GetEventsRequest) (*GetEventsResponse, error) AddEvent(context.Context, *AddEventRequest) (*AddEventResponse, error) - RemoveEvent(context.Context, *RemoveEventRequest) (*RemoveEventResponse, error) + RemoveEvent(context.Context, *RemoveEventRequest) (*GenericResponse, error) GetCryptocurrencyDepositAddresses(context.Context, *GetCryptocurrencyDepositAddressesRequest) (*GetCryptocurrencyDepositAddressesResponse, error) GetCryptocurrencyDepositAddress(context.Context, *GetCryptocurrencyDepositAddressRequest) (*GetCryptocurrencyDepositAddressResponse, error) WithdrawFiatFunds(context.Context, *WithdrawFiatRequest) (*WithdrawResponse, error) @@ -11741,215 +8504,247 @@ type GoCryptoTraderServer interface { GetLoggerDetails(context.Context, *GetLoggerDetailsRequest) (*GetLoggerDetailsResponse, error) SetLoggerDetails(context.Context, *SetLoggerDetailsRequest) (*GetLoggerDetailsResponse, error) GetExchangePairs(context.Context, *GetExchangePairsRequest) (*GetExchangePairsResponse, error) - EnableExchangePair(context.Context, *ExchangePairRequest) (*GenericExchangeNameResponse, error) - DisableExchangePair(context.Context, *ExchangePairRequest) (*GenericExchangeNameResponse, error) + SetExchangePair(context.Context, *SetExchangePairRequest) (*GenericResponse, error) GetOrderbookStream(*GetOrderbookStreamRequest, GoCryptoTrader_GetOrderbookStreamServer) error GetExchangeOrderbookStream(*GetExchangeOrderbookStreamRequest, GoCryptoTrader_GetExchangeOrderbookStreamServer) error GetTickerStream(*GetTickerStreamRequest, GoCryptoTrader_GetTickerStreamServer) error GetExchangeTickerStream(*GetExchangeTickerStreamRequest, GoCryptoTrader_GetExchangeTickerStreamServer) error GetAuditEvent(context.Context, *GetAuditEventRequest) (*GetAuditEventResponse, error) - GCTScriptExecute(context.Context, *GCTScriptExecuteRequest) (*GCTScriptGenericResponse, error) - GCTScriptUpload(context.Context, *GCTScriptUploadRequest) (*GCTScriptGenericResponse, error) + GCTScriptExecute(context.Context, *GCTScriptExecuteRequest) (*GenericResponse, error) + GCTScriptUpload(context.Context, *GCTScriptUploadRequest) (*GenericResponse, error) GCTScriptReadScript(context.Context, *GCTScriptReadScriptRequest) (*GCTScriptQueryResponse, error) GCTScriptStatus(context.Context, *GCTScriptStatusRequest) (*GCTScriptStatusResponse, error) GCTScriptQuery(context.Context, *GCTScriptQueryRequest) (*GCTScriptQueryResponse, error) - GCTScriptStop(context.Context, *GCTScriptStopRequest) (*GCTScriptGenericResponse, error) - GCTScriptStopAll(context.Context, *GCTScriptStopAllRequest) (*GCTScriptGenericResponse, error) + GCTScriptStop(context.Context, *GCTScriptStopRequest) (*GenericResponse, error) + GCTScriptStopAll(context.Context, *GCTScriptStopAllRequest) (*GenericResponse, error) GCTScriptListAll(context.Context, *GCTScriptListAllRequest) (*GCTScriptStatusResponse, error) - GCTScriptAutoLoadToggle(context.Context, *GCTScriptAutoLoadRequest) (*GCTScriptGenericResponse, error) + GCTScriptAutoLoadToggle(context.Context, *GCTScriptAutoLoadRequest) (*GenericResponse, error) GetHistoricCandles(context.Context, *GetHistoricCandlesRequest) (*GetHistoricCandlesResponse, error) + SetExchangeAsset(context.Context, *SetExchangeAssetRequest) (*GenericResponse, error) + SetAllExchangePairs(context.Context, *SetExchangeAllPairsRequest) (*GenericResponse, error) + UpdateExchangeSupportedPairs(context.Context, *UpdateExchangeSupportedPairsRequest) (*GenericResponse, error) + GetExchangeAssets(context.Context, *GetExchangeAssetsRequest) (*GetExchangeAssetsResponse, error) + WebsocketGetInfo(context.Context, *WebsocketGetInfoRequest) (*WebsocketGetInfoResponse, error) + WebsocketSetEnabled(context.Context, *WebsocketSetEnabledRequest) (*GenericResponse, error) + WebsocketGetSubscriptions(context.Context, *WebsocketGetSubscriptionsRequest) (*WebsocketGetSubscriptionsResponse, error) + WebsocketSetProxy(context.Context, *WebsocketSetProxyRequest) (*GenericResponse, error) + WebsocketSetURL(context.Context, *WebsocketSetURLRequest) (*GenericResponse, error) } // UnimplementedGoCryptoTraderServer can be embedded to have forward compatible implementations. type UnimplementedGoCryptoTraderServer struct { } -func (*UnimplementedGoCryptoTraderServer) GetInfo(context.Context, *GetInfoRequest) (*GetInfoResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetInfo(ctx context.Context, req *GetInfoRequest) (*GetInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetSubsystems(context.Context, *GetSubsystemsRequest) (*GetSusbsytemsResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetSubsystems(ctx context.Context, req *GetSubsystemsRequest) (*GetSusbsytemsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSubsystems not implemented") } -func (*UnimplementedGoCryptoTraderServer) EnableSubsystem(context.Context, *GenericSubsystemRequest) (*GenericSubsystemResponse, error) { +func (*UnimplementedGoCryptoTraderServer) EnableSubsystem(ctx context.Context, req *GenericSubsystemRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method EnableSubsystem not implemented") } -func (*UnimplementedGoCryptoTraderServer) DisableSubsystem(context.Context, *GenericSubsystemRequest) (*GenericSubsystemResponse, error) { +func (*UnimplementedGoCryptoTraderServer) DisableSubsystem(ctx context.Context, req *GenericSubsystemRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DisableSubsystem not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetRPCEndpoints(context.Context, *GetRPCEndpointsRequest) (*GetRPCEndpointsResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetRPCEndpoints(ctx context.Context, req *GetRPCEndpointsRequest) (*GetRPCEndpointsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetRPCEndpoints not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetCommunicationRelayers(context.Context, *GetCommunicationRelayersRequest) (*GetCommunicationRelayersResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetCommunicationRelayers(ctx context.Context, req *GetCommunicationRelayersRequest) (*GetCommunicationRelayersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetCommunicationRelayers not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetExchanges(context.Context, *GetExchangesRequest) (*GetExchangesResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetExchanges(ctx context.Context, req *GetExchangesRequest) (*GetExchangesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetExchanges not implemented") } -func (*UnimplementedGoCryptoTraderServer) DisableExchange(context.Context, *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) { +func (*UnimplementedGoCryptoTraderServer) DisableExchange(ctx context.Context, req *GenericExchangeNameRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DisableExchange not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetExchangeInfo(context.Context, *GenericExchangeNameRequest) (*GetExchangeInfoResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetExchangeInfo(ctx context.Context, req *GenericExchangeNameRequest) (*GetExchangeInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetExchangeInfo not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetExchangeOTPCode(context.Context, *GenericExchangeNameRequest) (*GetExchangeOTPReponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetExchangeOTPCode(ctx context.Context, req *GenericExchangeNameRequest) (*GetExchangeOTPReponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetExchangeOTPCode not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetExchangeOTPCodes(context.Context, *GetExchangeOTPsRequest) (*GetExchangeOTPsResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetExchangeOTPCodes(ctx context.Context, req *GetExchangeOTPsRequest) (*GetExchangeOTPsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetExchangeOTPCodes not implemented") } -func (*UnimplementedGoCryptoTraderServer) EnableExchange(context.Context, *GenericExchangeNameRequest) (*GenericExchangeNameResponse, error) { +func (*UnimplementedGoCryptoTraderServer) EnableExchange(ctx context.Context, req *GenericExchangeNameRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method EnableExchange not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetTicker(context.Context, *GetTickerRequest) (*TickerResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetTicker(ctx context.Context, req *GetTickerRequest) (*TickerResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTicker not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetTickers(context.Context, *GetTickersRequest) (*GetTickersResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetTickers(ctx context.Context, req *GetTickersRequest) (*GetTickersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTickers not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetOrderbook(context.Context, *GetOrderbookRequest) (*OrderbookResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetOrderbook(ctx context.Context, req *GetOrderbookRequest) (*OrderbookResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOrderbook not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetOrderbooks(context.Context, *GetOrderbooksRequest) (*GetOrderbooksResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetOrderbooks(ctx context.Context, req *GetOrderbooksRequest) (*GetOrderbooksResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOrderbooks not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetAccountInfo(context.Context, *GetAccountInfoRequest) (*GetAccountInfoResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetAccountInfo(ctx context.Context, req *GetAccountInfoRequest) (*GetAccountInfoResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetAccountInfo not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetAccountInfoStream(*GetAccountInfoRequest, GoCryptoTrader_GetAccountInfoStreamServer) error { +func (*UnimplementedGoCryptoTraderServer) GetAccountInfoStream(req *GetAccountInfoRequest, srv GoCryptoTrader_GetAccountInfoStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetAccountInfoStream not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetConfig(ctx context.Context, req *GetConfigRequest) (*GetConfigResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetPortfolio(context.Context, *GetPortfolioRequest) (*GetPortfolioResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetPortfolio(ctx context.Context, req *GetPortfolioRequest) (*GetPortfolioResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetPortfolio not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetPortfolioSummary(context.Context, *GetPortfolioSummaryRequest) (*GetPortfolioSummaryResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetPortfolioSummary(ctx context.Context, req *GetPortfolioSummaryRequest) (*GetPortfolioSummaryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetPortfolioSummary not implemented") } -func (*UnimplementedGoCryptoTraderServer) AddPortfolioAddress(context.Context, *AddPortfolioAddressRequest) (*AddPortfolioAddressResponse, error) { +func (*UnimplementedGoCryptoTraderServer) AddPortfolioAddress(ctx context.Context, req *AddPortfolioAddressRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddPortfolioAddress not implemented") } -func (*UnimplementedGoCryptoTraderServer) RemovePortfolioAddress(context.Context, *RemovePortfolioAddressRequest) (*RemovePortfolioAddressResponse, error) { +func (*UnimplementedGoCryptoTraderServer) RemovePortfolioAddress(ctx context.Context, req *RemovePortfolioAddressRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemovePortfolioAddress not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetForexProviders(context.Context, *GetForexProvidersRequest) (*GetForexProvidersResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetForexProviders(ctx context.Context, req *GetForexProvidersRequest) (*GetForexProvidersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetForexProviders not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetForexRates(context.Context, *GetForexRatesRequest) (*GetForexRatesResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetForexRates(ctx context.Context, req *GetForexRatesRequest) (*GetForexRatesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetForexRates not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetOrders(context.Context, *GetOrdersRequest) (*GetOrdersResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetOrders(ctx context.Context, req *GetOrdersRequest) (*GetOrdersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOrders not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetOrder(context.Context, *GetOrderRequest) (*OrderDetails, error) { +func (*UnimplementedGoCryptoTraderServer) GetOrder(ctx context.Context, req *GetOrderRequest) (*OrderDetails, error) { return nil, status.Errorf(codes.Unimplemented, "method GetOrder not implemented") } -func (*UnimplementedGoCryptoTraderServer) SubmitOrder(context.Context, *SubmitOrderRequest) (*SubmitOrderResponse, error) { +func (*UnimplementedGoCryptoTraderServer) SubmitOrder(ctx context.Context, req *SubmitOrderRequest) (*SubmitOrderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitOrder not implemented") } -func (*UnimplementedGoCryptoTraderServer) SimulateOrder(context.Context, *SimulateOrderRequest) (*SimulateOrderResponse, error) { +func (*UnimplementedGoCryptoTraderServer) SimulateOrder(ctx context.Context, req *SimulateOrderRequest) (*SimulateOrderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SimulateOrder not implemented") } -func (*UnimplementedGoCryptoTraderServer) WhaleBomb(context.Context, *WhaleBombRequest) (*SimulateOrderResponse, error) { +func (*UnimplementedGoCryptoTraderServer) WhaleBomb(ctx context.Context, req *WhaleBombRequest) (*SimulateOrderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WhaleBomb not implemented") } -func (*UnimplementedGoCryptoTraderServer) CancelOrder(context.Context, *CancelOrderRequest) (*CancelOrderResponse, error) { +func (*UnimplementedGoCryptoTraderServer) CancelOrder(ctx context.Context, req *CancelOrderRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CancelOrder not implemented") } -func (*UnimplementedGoCryptoTraderServer) CancelAllOrders(context.Context, *CancelAllOrdersRequest) (*CancelAllOrdersResponse, error) { +func (*UnimplementedGoCryptoTraderServer) CancelAllOrders(ctx context.Context, req *CancelAllOrdersRequest) (*CancelAllOrdersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CancelAllOrders not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetEvents(context.Context, *GetEventsRequest) (*GetEventsResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetEvents(ctx context.Context, req *GetEventsRequest) (*GetEventsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetEvents not implemented") } -func (*UnimplementedGoCryptoTraderServer) AddEvent(context.Context, *AddEventRequest) (*AddEventResponse, error) { +func (*UnimplementedGoCryptoTraderServer) AddEvent(ctx context.Context, req *AddEventRequest) (*AddEventResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddEvent not implemented") } -func (*UnimplementedGoCryptoTraderServer) RemoveEvent(context.Context, *RemoveEventRequest) (*RemoveEventResponse, error) { +func (*UnimplementedGoCryptoTraderServer) RemoveEvent(ctx context.Context, req *RemoveEventRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RemoveEvent not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetCryptocurrencyDepositAddresses(context.Context, *GetCryptocurrencyDepositAddressesRequest) (*GetCryptocurrencyDepositAddressesResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetCryptocurrencyDepositAddresses(ctx context.Context, req *GetCryptocurrencyDepositAddressesRequest) (*GetCryptocurrencyDepositAddressesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetCryptocurrencyDepositAddresses not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetCryptocurrencyDepositAddress(context.Context, *GetCryptocurrencyDepositAddressRequest) (*GetCryptocurrencyDepositAddressResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetCryptocurrencyDepositAddress(ctx context.Context, req *GetCryptocurrencyDepositAddressRequest) (*GetCryptocurrencyDepositAddressResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetCryptocurrencyDepositAddress not implemented") } -func (*UnimplementedGoCryptoTraderServer) WithdrawFiatFunds(context.Context, *WithdrawFiatRequest) (*WithdrawResponse, error) { +func (*UnimplementedGoCryptoTraderServer) WithdrawFiatFunds(ctx context.Context, req *WithdrawFiatRequest) (*WithdrawResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WithdrawFiatFunds not implemented") } -func (*UnimplementedGoCryptoTraderServer) WithdrawCryptocurrencyFunds(context.Context, *WithdrawCryptoRequest) (*WithdrawResponse, error) { +func (*UnimplementedGoCryptoTraderServer) WithdrawCryptocurrencyFunds(ctx context.Context, req *WithdrawCryptoRequest) (*WithdrawResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WithdrawCryptocurrencyFunds not implemented") } -func (*UnimplementedGoCryptoTraderServer) WithdrawalEventByID(context.Context, *WithdrawalEventByIDRequest) (*WithdrawalEventByIDResponse, error) { +func (*UnimplementedGoCryptoTraderServer) WithdrawalEventByID(ctx context.Context, req *WithdrawalEventByIDRequest) (*WithdrawalEventByIDResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WithdrawalEventByID not implemented") } -func (*UnimplementedGoCryptoTraderServer) WithdrawalEventsByExchange(context.Context, *WithdrawalEventsByExchangeRequest) (*WithdrawalEventsByExchangeResponse, error) { +func (*UnimplementedGoCryptoTraderServer) WithdrawalEventsByExchange(ctx context.Context, req *WithdrawalEventsByExchangeRequest) (*WithdrawalEventsByExchangeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WithdrawalEventsByExchange not implemented") } -func (*UnimplementedGoCryptoTraderServer) WithdrawalEventsByDate(context.Context, *WithdrawalEventsByDateRequest) (*WithdrawalEventsByExchangeResponse, error) { +func (*UnimplementedGoCryptoTraderServer) WithdrawalEventsByDate(ctx context.Context, req *WithdrawalEventsByDateRequest) (*WithdrawalEventsByExchangeResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WithdrawalEventsByDate not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetLoggerDetails(context.Context, *GetLoggerDetailsRequest) (*GetLoggerDetailsResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetLoggerDetails(ctx context.Context, req *GetLoggerDetailsRequest) (*GetLoggerDetailsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetLoggerDetails not implemented") } -func (*UnimplementedGoCryptoTraderServer) SetLoggerDetails(context.Context, *SetLoggerDetailsRequest) (*GetLoggerDetailsResponse, error) { +func (*UnimplementedGoCryptoTraderServer) SetLoggerDetails(ctx context.Context, req *SetLoggerDetailsRequest) (*GetLoggerDetailsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetLoggerDetails not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetExchangePairs(context.Context, *GetExchangePairsRequest) (*GetExchangePairsResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetExchangePairs(ctx context.Context, req *GetExchangePairsRequest) (*GetExchangePairsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetExchangePairs not implemented") } -func (*UnimplementedGoCryptoTraderServer) EnableExchangePair(context.Context, *ExchangePairRequest) (*GenericExchangeNameResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method EnableExchangePair not implemented") +func (*UnimplementedGoCryptoTraderServer) SetExchangePair(ctx context.Context, req *SetExchangePairRequest) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetExchangePair not implemented") } -func (*UnimplementedGoCryptoTraderServer) DisableExchangePair(context.Context, *ExchangePairRequest) (*GenericExchangeNameResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DisableExchangePair not implemented") -} -func (*UnimplementedGoCryptoTraderServer) GetOrderbookStream(*GetOrderbookStreamRequest, GoCryptoTrader_GetOrderbookStreamServer) error { +func (*UnimplementedGoCryptoTraderServer) GetOrderbookStream(req *GetOrderbookStreamRequest, srv GoCryptoTrader_GetOrderbookStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetOrderbookStream not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetExchangeOrderbookStream(*GetExchangeOrderbookStreamRequest, GoCryptoTrader_GetExchangeOrderbookStreamServer) error { +func (*UnimplementedGoCryptoTraderServer) GetExchangeOrderbookStream(req *GetExchangeOrderbookStreamRequest, srv GoCryptoTrader_GetExchangeOrderbookStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetExchangeOrderbookStream not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetTickerStream(*GetTickerStreamRequest, GoCryptoTrader_GetTickerStreamServer) error { +func (*UnimplementedGoCryptoTraderServer) GetTickerStream(req *GetTickerStreamRequest, srv GoCryptoTrader_GetTickerStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetTickerStream not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetExchangeTickerStream(*GetExchangeTickerStreamRequest, GoCryptoTrader_GetExchangeTickerStreamServer) error { +func (*UnimplementedGoCryptoTraderServer) GetExchangeTickerStream(req *GetExchangeTickerStreamRequest, srv GoCryptoTrader_GetExchangeTickerStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetExchangeTickerStream not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetAuditEvent(context.Context, *GetAuditEventRequest) (*GetAuditEventResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetAuditEvent(ctx context.Context, req *GetAuditEventRequest) (*GetAuditEventResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetAuditEvent not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptExecute(context.Context, *GCTScriptExecuteRequest) (*GCTScriptGenericResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptExecute(ctx context.Context, req *GCTScriptExecuteRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptExecute not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptUpload(context.Context, *GCTScriptUploadRequest) (*GCTScriptGenericResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptUpload(ctx context.Context, req *GCTScriptUploadRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptUpload not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptReadScript(context.Context, *GCTScriptReadScriptRequest) (*GCTScriptQueryResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptReadScript(ctx context.Context, req *GCTScriptReadScriptRequest) (*GCTScriptQueryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptReadScript not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptStatus(context.Context, *GCTScriptStatusRequest) (*GCTScriptStatusResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptStatus(ctx context.Context, req *GCTScriptStatusRequest) (*GCTScriptStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptStatus not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptQuery(context.Context, *GCTScriptQueryRequest) (*GCTScriptQueryResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptQuery(ctx context.Context, req *GCTScriptQueryRequest) (*GCTScriptQueryResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptQuery not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptStop(context.Context, *GCTScriptStopRequest) (*GCTScriptGenericResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptStop(ctx context.Context, req *GCTScriptStopRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptStop not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptStopAll(context.Context, *GCTScriptStopAllRequest) (*GCTScriptGenericResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptStopAll(ctx context.Context, req *GCTScriptStopAllRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptStopAll not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptListAll(context.Context, *GCTScriptListAllRequest) (*GCTScriptStatusResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptListAll(ctx context.Context, req *GCTScriptListAllRequest) (*GCTScriptStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptListAll not implemented") } -func (*UnimplementedGoCryptoTraderServer) GCTScriptAutoLoadToggle(context.Context, *GCTScriptAutoLoadRequest) (*GCTScriptGenericResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GCTScriptAutoLoadToggle(ctx context.Context, req *GCTScriptAutoLoadRequest) (*GenericResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GCTScriptAutoLoadToggle not implemented") } -func (*UnimplementedGoCryptoTraderServer) GetHistoricCandles(context.Context, *GetHistoricCandlesRequest) (*GetHistoricCandlesResponse, error) { +func (*UnimplementedGoCryptoTraderServer) GetHistoricCandles(ctx context.Context, req *GetHistoricCandlesRequest) (*GetHistoricCandlesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetHistoricCandles not implemented") } +func (*UnimplementedGoCryptoTraderServer) SetExchangeAsset(ctx context.Context, req *SetExchangeAssetRequest) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetExchangeAsset not implemented") +} +func (*UnimplementedGoCryptoTraderServer) SetAllExchangePairs(ctx context.Context, req *SetExchangeAllPairsRequest) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetAllExchangePairs not implemented") +} +func (*UnimplementedGoCryptoTraderServer) UpdateExchangeSupportedPairs(ctx context.Context, req *UpdateExchangeSupportedPairsRequest) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateExchangeSupportedPairs not implemented") +} +func (*UnimplementedGoCryptoTraderServer) GetExchangeAssets(ctx context.Context, req *GetExchangeAssetsRequest) (*GetExchangeAssetsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetExchangeAssets not implemented") +} +func (*UnimplementedGoCryptoTraderServer) WebsocketGetInfo(ctx context.Context, req *WebsocketGetInfoRequest) (*WebsocketGetInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WebsocketGetInfo not implemented") +} +func (*UnimplementedGoCryptoTraderServer) WebsocketSetEnabled(ctx context.Context, req *WebsocketSetEnabledRequest) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WebsocketSetEnabled not implemented") +} +func (*UnimplementedGoCryptoTraderServer) WebsocketGetSubscriptions(ctx context.Context, req *WebsocketGetSubscriptionsRequest) (*WebsocketGetSubscriptionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WebsocketGetSubscriptions not implemented") +} +func (*UnimplementedGoCryptoTraderServer) WebsocketSetProxy(ctx context.Context, req *WebsocketSetProxyRequest) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WebsocketSetProxy not implemented") +} +func (*UnimplementedGoCryptoTraderServer) WebsocketSetURL(ctx context.Context, req *WebsocketSetURLRequest) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method WebsocketSetURL not implemented") +} func RegisterGoCryptoTraderServer(s *grpc.Server, srv GoCryptoTraderServer) { s.RegisterService(&_GoCryptoTrader_serviceDesc, srv) @@ -12768,38 +9563,20 @@ func _GoCryptoTrader_GetExchangePairs_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } -func _GoCryptoTrader_EnableExchangePair_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ExchangePairRequest) +func _GoCryptoTrader_SetExchangePair_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetExchangePairRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(GoCryptoTraderServer).EnableExchangePair(ctx, in) + return srv.(GoCryptoTraderServer).SetExchangePair(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/gctrpc.GoCryptoTrader/EnableExchangePair", + FullMethod: "/gctrpc.GoCryptoTrader/SetExchangePair", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GoCryptoTraderServer).EnableExchangePair(ctx, req.(*ExchangePairRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _GoCryptoTrader_DisableExchangePair_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ExchangePairRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GoCryptoTraderServer).DisableExchangePair(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/gctrpc.GoCryptoTrader/DisableExchangePair", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GoCryptoTraderServer).DisableExchangePair(ctx, req.(*ExchangePairRequest)) + return srv.(GoCryptoTraderServer).SetExchangePair(ctx, req.(*SetExchangePairRequest)) } return interceptor(ctx, in, info, handler) } @@ -13086,6 +9863,168 @@ func _GoCryptoTrader_GetHistoricCandles_Handler(srv interface{}, ctx context.Con return interceptor(ctx, in, info, handler) } +func _GoCryptoTrader_SetExchangeAsset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetExchangeAssetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).SetExchangeAsset(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/SetExchangeAsset", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).SetExchangeAsset(ctx, req.(*SetExchangeAssetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_SetAllExchangePairs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetExchangeAllPairsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).SetAllExchangePairs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/SetAllExchangePairs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).SetAllExchangePairs(ctx, req.(*SetExchangeAllPairsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_UpdateExchangeSupportedPairs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateExchangeSupportedPairsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).UpdateExchangeSupportedPairs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/UpdateExchangeSupportedPairs", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).UpdateExchangeSupportedPairs(ctx, req.(*UpdateExchangeSupportedPairsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_GetExchangeAssets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetExchangeAssetsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).GetExchangeAssets(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/GetExchangeAssets", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).GetExchangeAssets(ctx, req.(*GetExchangeAssetsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_WebsocketGetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WebsocketGetInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).WebsocketGetInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/WebsocketGetInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).WebsocketGetInfo(ctx, req.(*WebsocketGetInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_WebsocketSetEnabled_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WebsocketSetEnabledRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).WebsocketSetEnabled(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/WebsocketSetEnabled", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).WebsocketSetEnabled(ctx, req.(*WebsocketSetEnabledRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_WebsocketGetSubscriptions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WebsocketGetSubscriptionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).WebsocketGetSubscriptions(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/WebsocketGetSubscriptions", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).WebsocketGetSubscriptions(ctx, req.(*WebsocketGetSubscriptionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_WebsocketSetProxy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WebsocketSetProxyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).WebsocketSetProxy(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/WebsocketSetProxy", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).WebsocketSetProxy(ctx, req.(*WebsocketSetProxyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GoCryptoTrader_WebsocketSetURL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WebsocketSetURLRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServer).WebsocketSetURL(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTrader/WebsocketSetURL", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServer).WebsocketSetURL(ctx, req.(*WebsocketSetURLRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _GoCryptoTrader_serviceDesc = grpc.ServiceDesc{ ServiceName: "gctrpc.GoCryptoTrader", HandlerType: (*GoCryptoTraderServer)(nil), @@ -13267,12 +10206,8 @@ var _GoCryptoTrader_serviceDesc = grpc.ServiceDesc{ Handler: _GoCryptoTrader_GetExchangePairs_Handler, }, { - MethodName: "EnableExchangePair", - Handler: _GoCryptoTrader_EnableExchangePair_Handler, - }, - { - MethodName: "DisableExchangePair", - Handler: _GoCryptoTrader_DisableExchangePair_Handler, + MethodName: "SetExchangePair", + Handler: _GoCryptoTrader_SetExchangePair_Handler, }, { MethodName: "GetAuditEvent", @@ -13318,6 +10253,42 @@ var _GoCryptoTrader_serviceDesc = grpc.ServiceDesc{ MethodName: "GetHistoricCandles", Handler: _GoCryptoTrader_GetHistoricCandles_Handler, }, + { + MethodName: "SetExchangeAsset", + Handler: _GoCryptoTrader_SetExchangeAsset_Handler, + }, + { + MethodName: "SetAllExchangePairs", + Handler: _GoCryptoTrader_SetAllExchangePairs_Handler, + }, + { + MethodName: "UpdateExchangeSupportedPairs", + Handler: _GoCryptoTrader_UpdateExchangeSupportedPairs_Handler, + }, + { + MethodName: "GetExchangeAssets", + Handler: _GoCryptoTrader_GetExchangeAssets_Handler, + }, + { + MethodName: "WebsocketGetInfo", + Handler: _GoCryptoTrader_WebsocketGetInfo_Handler, + }, + { + MethodName: "WebsocketSetEnabled", + Handler: _GoCryptoTrader_WebsocketSetEnabled_Handler, + }, + { + MethodName: "WebsocketGetSubscriptions", + Handler: _GoCryptoTrader_WebsocketGetSubscriptions_Handler, + }, + { + MethodName: "WebsocketSetProxy", + Handler: _GoCryptoTrader_WebsocketSetProxy_Handler, + }, + { + MethodName: "WebsocketSetURL", + Handler: _GoCryptoTrader_WebsocketSetURL_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/gctrpc/rpc.pb.gw.go b/gctrpc/rpc.pb.gw.go index 97f3615a..ae8bcfd6 100644 --- a/gctrpc/rpc.pb.gw.go +++ b/gctrpc/rpc.pb.gw.go @@ -91,10 +91,7 @@ func local_request_GoCryptoTrader_EnableSubsystem_0(ctx context.Context, marshal var protoReq GenericSubsystemRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_EnableSubsystem_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_EnableSubsystem_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -127,10 +124,7 @@ func local_request_GoCryptoTrader_DisableSubsystem_0(ctx context.Context, marsha var protoReq GenericSubsystemRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_DisableSubsystem_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_DisableSubsystem_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -199,10 +193,7 @@ func local_request_GoCryptoTrader_GetExchanges_0(ctx context.Context, marshaler var protoReq GetExchangesRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchanges_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetExchanges_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -269,10 +260,7 @@ func local_request_GoCryptoTrader_GetExchangeInfo_0(ctx context.Context, marshal var protoReq GenericExchangeNameRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchangeInfo_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetExchangeInfo_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -305,10 +293,7 @@ func local_request_GoCryptoTrader_GetExchangeOTPCode_0(ctx context.Context, mars var protoReq GenericExchangeNameRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchangeOTPCode_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetExchangeOTPCode_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -497,10 +482,7 @@ func local_request_GoCryptoTrader_GetAccountInfo_0(ctx context.Context, marshale var protoReq GetAccountInfoRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetAccountInfo_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetAccountInfo_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1281,10 +1263,7 @@ func local_request_GoCryptoTrader_GetLoggerDetails_0(ctx context.Context, marsha var protoReq GetLoggerDetailsRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetLoggerDetails_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetLoggerDetails_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1361,8 +1340,8 @@ func local_request_GoCryptoTrader_GetExchangePairs_0(ctx context.Context, marsha } -func request_GoCryptoTrader_EnableExchangePair_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ExchangePairRequest +func request_GoCryptoTrader_SetExchangePair_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SetExchangePairRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -1373,13 +1352,13 @@ func request_GoCryptoTrader_EnableExchangePair_0(ctx context.Context, marshaler return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := client.EnableExchangePair(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + msg, err := client.SetExchangePair(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return msg, metadata, err } -func local_request_GoCryptoTrader_EnableExchangePair_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ExchangePairRequest +func local_request_GoCryptoTrader_SetExchangePair_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SetExchangePairRequest var metadata runtime.ServerMetadata newReader, berr := utilities.IOReaderFactory(req.Body) @@ -1390,41 +1369,7 @@ func local_request_GoCryptoTrader_EnableExchangePair_0(ctx context.Context, mars return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } - msg, err := server.EnableExchangePair(ctx, &protoReq) - return msg, metadata, err - -} - -func request_GoCryptoTrader_DisableExchangePair_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ExchangePairRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.DisableExchangePair(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func local_request_GoCryptoTrader_DisableExchangePair_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq ExchangePairRequest - var metadata runtime.ServerMetadata - - newReader, berr := utilities.IOReaderFactory(req.Body) - if berr != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) - } - if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := server.DisableExchangePair(ctx, &protoReq) + msg, err := server.SetExchangePair(ctx, &protoReq) return msg, metadata, err } @@ -1565,10 +1510,7 @@ func local_request_GoCryptoTrader_GetAuditEvent_0(ctx context.Context, marshaler var protoReq GetAuditEventRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetAuditEvent_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetAuditEvent_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1601,10 +1543,7 @@ func local_request_GoCryptoTrader_GCTScriptExecute_0(ctx context.Context, marsha var protoReq GCTScriptExecuteRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GCTScriptExecute_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GCTScriptExecute_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1723,10 +1662,7 @@ func local_request_GoCryptoTrader_GCTScriptQuery_0(ctx context.Context, marshale var protoReq GCTScriptQueryRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GCTScriptQuery_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GCTScriptQuery_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1895,10 +1831,7 @@ func local_request_GoCryptoTrader_GetHistoricCandles_0(ctx context.Context, mars var protoReq GetHistoricCandlesRequest var metadata runtime.ServerMetadata - if err := req.ParseForm(); err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetHistoricCandles_0); err != nil { + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetHistoricCandles_0); err != nil { return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) } @@ -1907,6 +1840,303 @@ func local_request_GoCryptoTrader_GetHistoricCandles_0(ctx context.Context, mars } +var ( + filter_GoCryptoTrader_SetExchangeAsset_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_SetExchangeAsset_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SetExchangeAssetRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_SetExchangeAsset_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.SetExchangeAsset(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_SetExchangeAsset_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SetExchangeAssetRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_SetExchangeAsset_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.SetExchangeAsset(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_SetAllExchangePairs_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_SetAllExchangePairs_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SetExchangeAllPairsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_SetAllExchangePairs_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.SetAllExchangePairs(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_SetAllExchangePairs_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq SetExchangeAllPairsRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_SetAllExchangePairs_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.SetAllExchangePairs(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_UpdateExchangeSupportedPairs_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_UpdateExchangeSupportedPairs_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateExchangeSupportedPairsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_UpdateExchangeSupportedPairs_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.UpdateExchangeSupportedPairs(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_UpdateExchangeSupportedPairs_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq UpdateExchangeSupportedPairsRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_UpdateExchangeSupportedPairs_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.UpdateExchangeSupportedPairs(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_GetExchangeAssets_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_GetExchangeAssets_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetExchangeAssetsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchangeAssets_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetExchangeAssets(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_GetExchangeAssets_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetExchangeAssetsRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetExchangeAssets_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetExchangeAssets(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_WebsocketGetInfo_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_WebsocketGetInfo_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketGetInfoRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_WebsocketGetInfo_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.WebsocketGetInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_WebsocketGetInfo_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketGetInfoRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_WebsocketGetInfo_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.WebsocketGetInfo(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_WebsocketSetEnabled_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_WebsocketSetEnabled_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketSetEnabledRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_WebsocketSetEnabled_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.WebsocketSetEnabled(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_WebsocketSetEnabled_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketSetEnabledRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_WebsocketSetEnabled_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.WebsocketSetEnabled(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_WebsocketGetSubscriptions_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_WebsocketGetSubscriptions_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketGetSubscriptionsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_WebsocketGetSubscriptions_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.WebsocketGetSubscriptions(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_WebsocketGetSubscriptions_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketGetSubscriptionsRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_WebsocketGetSubscriptions_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.WebsocketGetSubscriptions(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_WebsocketSetProxy_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_WebsocketSetProxy_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketSetProxyRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_WebsocketSetProxy_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.WebsocketSetProxy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_WebsocketSetProxy_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketSetProxyRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_WebsocketSetProxy_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.WebsocketSetProxy(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_GoCryptoTrader_WebsocketSetURL_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTrader_WebsocketSetURL_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketSetURLRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_WebsocketSetURL_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.WebsocketSetURL(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTrader_WebsocketSetURL_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq WebsocketSetURLRequest + var metadata runtime.ServerMetadata + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_WebsocketSetURL_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.WebsocketSetURL(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterGoCryptoTraderHandlerServer registers the http handlers for service GoCryptoTrader to "mux". // UnaryRPC :call GoCryptoTraderServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -2799,7 +3029,7 @@ func RegisterGoCryptoTraderHandlerServer(ctx context.Context, mux *runtime.Serve }) - mux.Handle("POST", pattern_GoCryptoTrader_EnableExchangePair_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_GoCryptoTrader_SetExchangePair_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -2808,34 +3038,14 @@ func RegisterGoCryptoTraderHandlerServer(ctx context.Context, mux *runtime.Serve runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_GoCryptoTrader_EnableExchangePair_0(rctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_GoCryptoTrader_SetExchangePair_0(rctx, inboundMarshaler, server, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_GoCryptoTrader_EnableExchangePair_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_GoCryptoTrader_DisableExchangePair_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := local_request_GoCryptoTrader_DisableExchangePair_0(rctx, inboundMarshaler, server, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_GoCryptoTrader_DisableExchangePair_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_GoCryptoTrader_SetExchangePair_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -3087,6 +3297,186 @@ func RegisterGoCryptoTraderHandlerServer(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_SetExchangeAsset_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_SetExchangeAsset_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_SetExchangeAsset_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_SetAllExchangePairs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_SetAllExchangePairs_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_SetAllExchangePairs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_UpdateExchangeSupportedPairs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_UpdateExchangeSupportedPairs_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_UpdateExchangeSupportedPairs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_GetExchangeAssets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_GetExchangeAssets_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetExchangeAssets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketGetInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_WebsocketGetInfo_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketGetInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketSetEnabled_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_WebsocketSetEnabled_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketSetEnabled_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketGetSubscriptions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_WebsocketGetSubscriptions_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketGetSubscriptions_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketSetProxy_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_WebsocketSetProxy_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketSetProxy_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketSetURL_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTrader_WebsocketSetURL_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketSetURL_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -4028,7 +4418,7 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve }) - mux.Handle("POST", pattern_GoCryptoTrader_EnableExchangePair_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_GoCryptoTrader_SetExchangePair_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) @@ -4037,34 +4427,14 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_GoCryptoTrader_EnableExchangePair_0(rctx, inboundMarshaler, client, req, pathParams) + resp, md, err := request_GoCryptoTrader_SetExchangePair_0(rctx, inboundMarshaler, client, req, pathParams) ctx = runtime.NewServerMetadataContext(ctx, md) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - forward_GoCryptoTrader_EnableExchangePair_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_GoCryptoTrader_DisableExchangePair_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_GoCryptoTrader_DisableExchangePair_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_GoCryptoTrader_DisableExchangePair_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_GoCryptoTrader_SetExchangePair_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -4368,6 +4738,186 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve }) + mux.Handle("GET", pattern_GoCryptoTrader_SetExchangeAsset_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_SetExchangeAsset_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_SetExchangeAsset_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_SetAllExchangePairs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_SetAllExchangePairs_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_SetAllExchangePairs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_UpdateExchangeSupportedPairs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_UpdateExchangeSupportedPairs_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_UpdateExchangeSupportedPairs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_GetExchangeAssets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_GetExchangeAssets_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_GetExchangeAssets_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketGetInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_WebsocketGetInfo_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketGetInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketSetEnabled_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_WebsocketSetEnabled_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketSetEnabled_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketGetSubscriptions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_WebsocketGetSubscriptions_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketGetSubscriptions_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketSetProxy_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_WebsocketSetProxy_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketSetProxy_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_GoCryptoTrader_WebsocketSetURL_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTrader_WebsocketSetURL_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTrader_WebsocketSetURL_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -4462,9 +5012,7 @@ var ( pattern_GoCryptoTrader_GetExchangePairs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getexchangepairs"}, "", runtime.AssumeColonVerbOpt(true))) - pattern_GoCryptoTrader_EnableExchangePair_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "enableexchangepair"}, "", runtime.AssumeColonVerbOpt(true))) - - pattern_GoCryptoTrader_DisableExchangePair_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "disableexchangepair"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_GoCryptoTrader_SetExchangePair_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "setexchangepair"}, "", runtime.AssumeColonVerbOpt(true))) pattern_GoCryptoTrader_GetOrderbookStream_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getorderbookstream"}, "", runtime.AssumeColonVerbOpt(true))) @@ -4495,6 +5043,24 @@ var ( pattern_GoCryptoTrader_GCTScriptAutoLoadToggle_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "gctscript", "autoload"}, "", runtime.AssumeColonVerbOpt(true))) pattern_GoCryptoTrader_GetHistoricCandles_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "gethistoriccandles"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_SetExchangeAsset_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "setexchangeasset"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_SetAllExchangePairs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "setallexchangepairs"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_UpdateExchangeSupportedPairs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "updateexchangesupportedpairs"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_GetExchangeAssets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getexchangeassets"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_WebsocketGetInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "websocketgetinfo"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_WebsocketSetEnabled_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "websocketsetenabled"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_WebsocketGetSubscriptions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "websocketgetsubscriptions"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_WebsocketSetProxy_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "websocketsetproxy"}, "", runtime.AssumeColonVerbOpt(true))) + + pattern_GoCryptoTrader_WebsocketSetURL_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "websocketseturl"}, "", runtime.AssumeColonVerbOpt(true))) ) var ( @@ -4588,9 +5154,7 @@ var ( forward_GoCryptoTrader_GetExchangePairs_0 = runtime.ForwardResponseMessage - forward_GoCryptoTrader_EnableExchangePair_0 = runtime.ForwardResponseMessage - - forward_GoCryptoTrader_DisableExchangePair_0 = runtime.ForwardResponseMessage + forward_GoCryptoTrader_SetExchangePair_0 = runtime.ForwardResponseMessage forward_GoCryptoTrader_GetOrderbookStream_0 = runtime.ForwardResponseStream @@ -4621,4 +5185,22 @@ var ( forward_GoCryptoTrader_GCTScriptAutoLoadToggle_0 = runtime.ForwardResponseMessage forward_GoCryptoTrader_GetHistoricCandles_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_SetExchangeAsset_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_SetAllExchangePairs_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_UpdateExchangeSupportedPairs_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_GetExchangeAssets_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_WebsocketGetInfo_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_WebsocketSetEnabled_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_WebsocketGetSubscriptions_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_WebsocketSetProxy_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTrader_WebsocketSetURL_0 = runtime.ForwardResponseMessage ) diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index b5e0e3c7..fe9292ce 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -32,8 +32,6 @@ message GenericSubsystemRequest { string subsystem = 1; } -message GenericSubsystemResponse {} - message GetSubsystemsRequest {} message GetSusbsytemsResponse { @@ -55,8 +53,6 @@ message GenericExchangeNameRequest { string exchange = 1; } -message GenericExchangeNameResponse {} - message GetExchangesRequest { bool enabled = 1; } @@ -249,16 +245,12 @@ message AddPortfolioAddressRequest { bool cold_storage = 6; } -message AddPortfolioAddressResponse {} - message RemovePortfolioAddressRequest { string address = 1; string coin_type = 2; string description = 3; } -message RemovePortfolioAddressResponse {} - message GetForexProvidersRequest {} message ForexProvider { @@ -379,8 +371,6 @@ message CancelOrderRequest { string side = 7; } -message CancelOrderResponse {} - message CancelAllOrdersRequest { string exchange = 1; } @@ -431,8 +421,6 @@ message RemoveEventRequest { int64 id = 1; } -message RemoveEventResponse {} - message GetCryptocurrencyDepositAddressesRequest { string exchange = 1; } @@ -561,10 +549,11 @@ message GetExchangePairsResponse { map supported_assets = 1; } -message ExchangePairRequest { +message SetExchangePairRequest { string exchange = 1; string asset_type = 2; - CurrencyPair pair = 3; + repeated CurrencyPair pairs = 3; + bool enable = 4; } message GetOrderbookStreamRequest { @@ -685,11 +674,79 @@ message GCTScriptQueryResponse{ string data = 3; } -message GCTScriptGenericResponse { +message GenericResponse { string status = 1; string data = 2; } +message SetExchangeAssetRequest { + string exchange = 1; + string asset = 2; + bool enable = 3; +} + +message SetExchangeAllPairsRequest { + string exchange = 1; + bool enable = 2; +} + +message UpdateExchangeSupportedPairsRequest { + string exchange = 1; +} + +message GetExchangeAssetsRequest { + string exchange = 1; +} + +message GetExchangeAssetsResponse { + string assets = 1; +} + +message WebsocketGetInfoRequest { + string exchange = 1; +} + +message WebsocketGetInfoResponse { + string exchange = 1; + bool supported = 2; + bool enabled = 3; + bool authenticated_supported = 4; + bool authenticated = 5; + string running_url = 6; + string proxy_address = 7; +} + +message WebsocketSetEnabledRequest { + string exchange = 1; + bool enable = 2; +} + +message WebsocketGetSubscriptionsRequest { + string exchange = 1; +} + +message WebsocketSubscription { + string channel = 1; + string currency = 2; + string asset = 3; + string params = 4; +} + +message WebsocketGetSubscriptionsResponse { + string exchange = 1; + repeated WebsocketSubscription subscriptions = 2; +} + +message WebsocketSetProxyRequest { + string exchange = 1; + string proxy = 2; +} + +message WebsocketSetURLRequest { + string exchange = 1; + string url = 2; +} + service GoCryptoTrader { rpc GetInfo (GetInfoRequest) returns (GetInfoResponse) { option (google.api.http) = { @@ -703,13 +760,13 @@ service GoCryptoTrader { }; } - rpc EnableSubsystem (GenericSubsystemRequest) returns (GenericSubsystemResponse) { + rpc EnableSubsystem (GenericSubsystemRequest) returns (GenericResponse) { option (google.api.http) = { get: "/v1/enablesubsystem" }; } - rpc DisableSubsystem (GenericSubsystemRequest) returns (GenericSubsystemResponse) { + rpc DisableSubsystem (GenericSubsystemRequest) returns (GenericResponse) { option (google.api.http) = { get: "/v1/disablesubsystem" }; @@ -733,7 +790,7 @@ service GoCryptoTrader { }; } - rpc DisableExchange (GenericExchangeNameRequest) returns (GenericExchangeNameResponse) { + rpc DisableExchange (GenericExchangeNameRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/disableexchange" body: "*" @@ -758,7 +815,7 @@ service GoCryptoTrader { }; } - rpc EnableExchange (GenericExchangeNameRequest) returns (GenericExchangeNameResponse) { + rpc EnableExchange (GenericExchangeNameRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/enableexchange" body: "*" @@ -822,14 +879,14 @@ service GoCryptoTrader { } - rpc AddPortfolioAddress (AddPortfolioAddressRequest) returns (AddPortfolioAddressResponse) { + rpc AddPortfolioAddress (AddPortfolioAddressRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/addportfolioaddress" body: "*" }; } - rpc RemovePortfolioAddress (RemovePortfolioAddressRequest) returns (RemovePortfolioAddressResponse) { + rpc RemovePortfolioAddress (RemovePortfolioAddressRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/removeportfolioaddress" body: "*" @@ -883,7 +940,7 @@ service GoCryptoTrader { }; } - rpc CancelOrder (CancelOrderRequest) returns (CancelOrderResponse) { + rpc CancelOrder (CancelOrderRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/cancelorder" body: "*" @@ -910,7 +967,7 @@ service GoCryptoTrader { }; } - rpc RemoveEvent(RemoveEventRequest) returns (RemoveEventResponse) { + rpc RemoveEvent(RemoveEventRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/removeevent" body: "*" @@ -987,16 +1044,9 @@ service GoCryptoTrader { }; } - rpc EnableExchangePair(ExchangePairRequest) returns (GenericExchangeNameResponse) { + rpc SetExchangePair(SetExchangePairRequest) returns (GenericResponse) { option (google.api.http) = { - post: "/v1/enableexchangepair", - body: "*" - }; - } - - rpc DisableExchangePair(ExchangePairRequest) returns (GenericExchangeNameResponse) { - option (google.api.http) = { - post: "/v1/disableexchangepair", + post: "/v1/setexchangepair", body: "*" }; } @@ -1031,13 +1081,13 @@ service GoCryptoTrader { }; } - rpc GCTScriptExecute(GCTScriptExecuteRequest) returns (GCTScriptGenericResponse) { + rpc GCTScriptExecute(GCTScriptExecuteRequest) returns (GenericResponse) { option (google.api.http) = { get: "/v1/gctscript/execute", }; } - rpc GCTScriptUpload(GCTScriptUploadRequest) returns (GCTScriptGenericResponse) { + rpc GCTScriptUpload(GCTScriptUploadRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/gctscript/upload", body: "*" @@ -1063,14 +1113,14 @@ service GoCryptoTrader { }; } - rpc GCTScriptStop(GCTScriptStopRequest) returns (GCTScriptGenericResponse) { + rpc GCTScriptStop(GCTScriptStopRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/gctscript/stop", body: "*" }; } - rpc GCTScriptStopAll(GCTScriptStopAllRequest) returns (GCTScriptGenericResponse) { + rpc GCTScriptStopAll(GCTScriptStopAllRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/gctscript/stop", body: "*" @@ -1083,7 +1133,7 @@ service GoCryptoTrader { body: "*" }; } - rpc GCTScriptAutoLoadToggle(GCTScriptAutoLoadRequest) returns (GCTScriptGenericResponse) { + rpc GCTScriptAutoLoadToggle(GCTScriptAutoLoadRequest) returns (GenericResponse) { option (google.api.http) = { post: "/v1/gctscript/autoload", body: "*" @@ -1095,4 +1145,58 @@ service GoCryptoTrader { get: "/v1/gethistoriccandles" }; } -} + + rpc SetExchangeAsset(SetExchangeAssetRequest) returns (GenericResponse) { + option (google.api.http) = { + get: "/v1/setexchangeasset" + }; + } + + rpc SetAllExchangePairs(SetExchangeAllPairsRequest) returns (GenericResponse) { + option (google.api.http) = { + get: "/v1/setallexchangepairs" + }; + } + + rpc UpdateExchangeSupportedPairs(UpdateExchangeSupportedPairsRequest) returns (GenericResponse) { + option (google.api.http) = { + get: "/v1/updateexchangesupportedpairs" + }; + } + + rpc GetExchangeAssets(GetExchangeAssetsRequest) returns (GetExchangeAssetsResponse) { + option (google.api.http) = { + get: "/v1/getexchangeassets" + }; + } + + rpc WebsocketGetInfo(WebsocketGetInfoRequest) returns (WebsocketGetInfoResponse) { + option (google.api.http) = { + get: "/v1/websocketgetinfo" + }; + } + + rpc WebsocketSetEnabled(WebsocketSetEnabledRequest) returns (GenericResponse) { + option (google.api.http) = { + get: "/v1/websocketsetenabled" + }; + } + + rpc WebsocketGetSubscriptions(WebsocketGetSubscriptionsRequest) returns (WebsocketGetSubscriptionsResponse) { + option (google.api.http) = { + get: "/v1/websocketgetsubscriptions" + }; + } + + rpc WebsocketSetProxy(WebsocketSetProxyRequest) returns (GenericResponse) { + option (google.api.http) = { + get: "/v1/websocketsetproxy" + }; + } + + rpc WebsocketSetURL(WebsocketSetURLRequest) returns (GenericResponse) { + option (google.api.http) = { + get: "/v1/websocketseturl" + }; + } +} \ No newline at end of file diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index 057a7351..45b0f0f6 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -13,7 +13,7 @@ "paths": { "/v1/addevent": { "post": { - "operationId": "GoCryptoTrader_AddEvent", + "operationId": "AddEvent", "responses": { "200": { "description": "A successful response.", @@ -45,12 +45,12 @@ }, "/v1/addportfolioaddress": { "post": { - "operationId": "GoCryptoTrader_AddPortfolioAddress", + "operationId": "AddPortfolioAddress", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcAddPortfolioAddressResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -77,7 +77,7 @@ }, "/v1/cancelallorders": { "post": { - "operationId": "GoCryptoTrader_CancelAllOrders", + "operationId": "CancelAllOrders", "responses": { "200": { "description": "A successful response.", @@ -109,12 +109,12 @@ }, "/v1/cancelorder": { "post": { - "operationId": "GoCryptoTrader_CancelOrder", + "operationId": "CancelOrder", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcCancelOrderResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -141,12 +141,12 @@ }, "/v1/disableexchange": { "post": { - "operationId": "GoCryptoTrader_DisableExchange", + "operationId": "DisableExchange", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcGenericExchangeNameResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -171,46 +171,14 @@ ] } }, - "/v1/disableexchangepair": { - "post": { - "operationId": "GoCryptoTrader_DisableExchangePair", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/gctrpcGenericExchangeNameResponse" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/gctrpcExchangePairRequest" - } - } - ], - "tags": [ - "GoCryptoTrader" - ] - } - }, "/v1/disablesubsystem": { "get": { - "operationId": "GoCryptoTrader_DisableSubsystem", + "operationId": "DisableSubsystem", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcGenericSubsystemResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -235,12 +203,12 @@ }, "/v1/enableexchange": { "post": { - "operationId": "GoCryptoTrader_EnableExchange", + "operationId": "EnableExchange", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcGenericExchangeNameResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -265,46 +233,14 @@ ] } }, - "/v1/enableexchangepair": { - "post": { - "operationId": "GoCryptoTrader_EnableExchangePair", - "responses": { - "200": { - "description": "A successful response.", - "schema": { - "$ref": "#/definitions/gctrpcGenericExchangeNameResponse" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/runtimeError" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/gctrpcExchangePairRequest" - } - } - ], - "tags": [ - "GoCryptoTrader" - ] - } - }, "/v1/enablesubsystem": { "get": { - "operationId": "GoCryptoTrader_EnableSubsystem", + "operationId": "EnableSubsystem", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcGenericSubsystemResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -329,12 +265,12 @@ }, "/v1/gctscript/autoload": { "post": { - "operationId": "GoCryptoTrader_GCTScriptAutoLoadToggle", + "operationId": "GCTScriptAutoLoadToggle", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcGCTScriptGenericResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -361,12 +297,12 @@ }, "/v1/gctscript/execute": { "get": { - "operationId": "GoCryptoTrader_GCTScriptExecute", + "operationId": "GCTScriptExecute", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcGCTScriptGenericResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -409,7 +345,7 @@ }, "/v1/gctscript/query": { "get": { - "operationId": "GoCryptoTrader_GCTScriptQuery", + "operationId": "GCTScriptQuery", "responses": { "200": { "description": "A successful response.", @@ -457,7 +393,7 @@ }, "/v1/gctscript/read": { "post": { - "operationId": "GoCryptoTrader_GCTScriptReadScript", + "operationId": "GCTScriptReadScript", "responses": { "200": { "description": "A successful response.", @@ -489,7 +425,7 @@ }, "/v1/gctscript/status": { "get": { - "operationId": "GoCryptoTrader_GCTScriptStatus", + "operationId": "GCTScriptStatus", "responses": { "200": { "description": "A successful response.", @@ -511,7 +447,7 @@ }, "/v1/gctscript/stop": { "post": { - "operationId": "GoCryptoTrader_GCTScriptListAll", + "operationId": "GCTScriptListAll", "responses": { "200": { "description": "A successful response.", @@ -543,12 +479,12 @@ }, "/v1/gctscript/upload": { "post": { - "operationId": "GoCryptoTrader_GCTScriptUpload", + "operationId": "GCTScriptUpload", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcGCTScriptGenericResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -575,7 +511,7 @@ }, "/v1/getaccountinfo": { "get": { - "operationId": "GoCryptoTrader_GetAccountInfo", + "operationId": "GetAccountInfo", "responses": { "200": { "description": "A successful response.", @@ -605,7 +541,7 @@ }, "/v1/getaccountinfostream": { "get": { - "operationId": "GoCryptoTrader_GetAccountInfoStream", + "operationId": "GetAccountInfoStream", "responses": { "200": { "description": "A successful response.(streaming responses)", @@ -644,7 +580,7 @@ }, "/v1/getauditevent": { "get": { - "operationId": "GoCryptoTrader_GetAuditEvent", + "operationId": "GetAuditEvent", "responses": { "200": { "description": "A successful response.", @@ -700,7 +636,7 @@ }, "/v1/getcommunicationrelayers": { "get": { - "operationId": "GoCryptoTrader_GetCommunicationRelayers", + "operationId": "GetCommunicationRelayers", "responses": { "200": { "description": "A successful response.", @@ -722,7 +658,7 @@ }, "/v1/getconfig": { "get": { - "operationId": "GoCryptoTrader_GetConfig", + "operationId": "GetConfig", "responses": { "200": { "description": "A successful response.", @@ -744,7 +680,7 @@ }, "/v1/getcryptodepositaddress": { "post": { - "operationId": "GoCryptoTrader_GetCryptocurrencyDepositAddress", + "operationId": "GetCryptocurrencyDepositAddress", "responses": { "200": { "description": "A successful response.", @@ -776,7 +712,7 @@ }, "/v1/getcryptodepositaddresses": { "post": { - "operationId": "GoCryptoTrader_GetCryptocurrencyDepositAddresses", + "operationId": "GetCryptocurrencyDepositAddresses", "responses": { "200": { "description": "A successful response.", @@ -808,7 +744,7 @@ }, "/v1/getevents": { "get": { - "operationId": "GoCryptoTrader_GetEvents", + "operationId": "GetEvents", "responses": { "200": { "description": "A successful response.", @@ -828,9 +764,39 @@ ] } }, + "/v1/getexchangeassets": { + "get": { + "operationId": "GetExchangeAssets", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGetExchangeAssetsResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, "/v1/getexchangeinfo": { "get": { - "operationId": "GoCryptoTrader_GetExchangeInfo", + "operationId": "GetExchangeInfo", "responses": { "200": { "description": "A successful response.", @@ -860,7 +826,7 @@ }, "/v1/getexchangeorderbookstream": { "get": { - "operationId": "GoCryptoTrader_GetExchangeOrderbookStream", + "operationId": "GetExchangeOrderbookStream", "responses": { "200": { "description": "A successful response.(streaming responses)", @@ -899,7 +865,7 @@ }, "/v1/getexchangeotp": { "get": { - "operationId": "GoCryptoTrader_GetExchangeOTPCode", + "operationId": "GetExchangeOTPCode", "responses": { "200": { "description": "A successful response.", @@ -929,7 +895,7 @@ }, "/v1/getexchangeotps": { "get": { - "operationId": "GoCryptoTrader_GetExchangeOTPCodes", + "operationId": "GetExchangeOTPCodes", "responses": { "200": { "description": "A successful response.", @@ -951,7 +917,7 @@ }, "/v1/getexchangepairs": { "post": { - "operationId": "GoCryptoTrader_GetExchangePairs", + "operationId": "GetExchangePairs", "responses": { "200": { "description": "A successful response.", @@ -983,7 +949,7 @@ }, "/v1/getexchanges": { "get": { - "operationId": "GoCryptoTrader_GetExchanges", + "operationId": "GetExchanges", "responses": { "200": { "description": "A successful response.", @@ -1014,7 +980,7 @@ }, "/v1/getexchangetickerstream": { "get": { - "operationId": "GoCryptoTrader_GetExchangeTickerStream", + "operationId": "GetExchangeTickerStream", "responses": { "200": { "description": "A successful response.(streaming responses)", @@ -1053,7 +1019,7 @@ }, "/v1/getforexproviders": { "get": { - "operationId": "GoCryptoTrader_GetForexProviders", + "operationId": "GetForexProviders", "responses": { "200": { "description": "A successful response.", @@ -1075,7 +1041,7 @@ }, "/v1/getforexrates": { "get": { - "operationId": "GoCryptoTrader_GetForexRates", + "operationId": "GetForexRates", "responses": { "200": { "description": "A successful response.", @@ -1097,7 +1063,7 @@ }, "/v1/gethistoriccandles": { "get": { - "operationId": "GoCryptoTrader_GetHistoricCandles", + "operationId": "GetHistoricCandles", "responses": { "200": { "description": "A successful response.", @@ -1179,7 +1145,7 @@ }, "/v1/getinfo": { "get": { - "operationId": "GoCryptoTrader_GetInfo", + "operationId": "GetInfo", "responses": { "200": { "description": "A successful response.", @@ -1201,7 +1167,7 @@ }, "/v1/getloggerdetails": { "get": { - "operationId": "GoCryptoTrader_GetLoggerDetails", + "operationId": "GetLoggerDetails", "responses": { "200": { "description": "A successful response.", @@ -1231,7 +1197,7 @@ }, "/v1/getorder": { "post": { - "operationId": "GoCryptoTrader_GetOrder", + "operationId": "GetOrder", "responses": { "200": { "description": "A successful response.", @@ -1263,7 +1229,7 @@ }, "/v1/getorderbook": { "post": { - "operationId": "GoCryptoTrader_GetOrderbook", + "operationId": "GetOrderbook", "responses": { "200": { "description": "A successful response.", @@ -1295,7 +1261,7 @@ }, "/v1/getorderbooks": { "get": { - "operationId": "GoCryptoTrader_GetOrderbooks", + "operationId": "GetOrderbooks", "responses": { "200": { "description": "A successful response.", @@ -1317,7 +1283,7 @@ }, "/v1/getorderbookstream": { "get": { - "operationId": "GoCryptoTrader_GetOrderbookStream", + "operationId": "GetOrderbookStream", "responses": { "200": { "description": "A successful response.(streaming responses)", @@ -1380,7 +1346,7 @@ }, "/v1/getorders": { "post": { - "operationId": "GoCryptoTrader_GetOrders", + "operationId": "GetOrders", "responses": { "200": { "description": "A successful response.", @@ -1412,7 +1378,7 @@ }, "/v1/getportfolio": { "get": { - "operationId": "GoCryptoTrader_GetPortfolio", + "operationId": "GetPortfolio", "responses": { "200": { "description": "A successful response.", @@ -1434,7 +1400,7 @@ }, "/v1/getportfoliosummary": { "get": { - "operationId": "GoCryptoTrader_GetPortfolioSummary", + "operationId": "GetPortfolioSummary", "responses": { "200": { "description": "A successful response.", @@ -1456,7 +1422,7 @@ }, "/v1/getrpcendpoints": { "get": { - "operationId": "GoCryptoTrader_GetRPCEndpoints", + "operationId": "GetRPCEndpoints", "responses": { "200": { "description": "A successful response.", @@ -1478,7 +1444,7 @@ }, "/v1/getsubsystems": { "get": { - "operationId": "GoCryptoTrader_GetSubsystems", + "operationId": "GetSubsystems", "responses": { "200": { "description": "A successful response.", @@ -1500,7 +1466,7 @@ }, "/v1/getticker": { "post": { - "operationId": "GoCryptoTrader_GetTicker", + "operationId": "GetTicker", "responses": { "200": { "description": "A successful response.", @@ -1532,7 +1498,7 @@ }, "/v1/gettickers": { "get": { - "operationId": "GoCryptoTrader_GetTickers", + "operationId": "GetTickers", "responses": { "200": { "description": "A successful response.", @@ -1554,7 +1520,7 @@ }, "/v1/gettickerstream": { "get": { - "operationId": "GoCryptoTrader_GetTickerStream", + "operationId": "GetTickerStream", "responses": { "200": { "description": "A successful response.(streaming responses)", @@ -1617,12 +1583,12 @@ }, "/v1/removeevent": { "post": { - "operationId": "GoCryptoTrader_RemoveEvent", + "operationId": "RemoveEvent", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcRemoveEventResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -1649,12 +1615,12 @@ }, "/v1/removeportfolioaddress": { "post": { - "operationId": "GoCryptoTrader_RemovePortfolioAddress", + "operationId": "RemovePortfolioAddress", "responses": { "200": { "description": "A successful response.", "schema": { - "$ref": "#/definitions/gctrpcRemovePortfolioAddressResponse" + "$ref": "#/definitions/gctrpcGenericResponse" } }, "default": { @@ -1679,9 +1645,121 @@ ] } }, + "/v1/setallexchangepairs": { + "get": { + "operationId": "SetAllExchangePairs", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGenericResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "enable", + "in": "query", + "required": false, + "type": "boolean", + "format": "boolean" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, + "/v1/setexchangeasset": { + "get": { + "operationId": "SetExchangeAsset", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGenericResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "asset", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "enable", + "in": "query", + "required": false, + "type": "boolean", + "format": "boolean" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, + "/v1/setexchangepair": { + "post": { + "operationId": "SetExchangePair", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGenericResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/gctrpcSetExchangePairRequest" + } + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, "/v1/setloggerdetails": { "post": { - "operationId": "GoCryptoTrader_SetLoggerDetails", + "operationId": "SetLoggerDetails", "responses": { "200": { "description": "A successful response.", @@ -1713,7 +1791,7 @@ }, "/v1/simulateorder": { "post": { - "operationId": "GoCryptoTrader_SimulateOrder", + "operationId": "SimulateOrder", "responses": { "200": { "description": "A successful response.", @@ -1745,7 +1823,7 @@ }, "/v1/submitorder": { "post": { - "operationId": "GoCryptoTrader_SubmitOrder", + "operationId": "SubmitOrder", "responses": { "200": { "description": "A successful response.", @@ -1775,9 +1853,208 @@ ] } }, + "/v1/updateexchangesupportedpairs": { + "get": { + "operationId": "UpdateExchangeSupportedPairs", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGenericResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, + "/v1/websocketgetinfo": { + "get": { + "operationId": "WebsocketGetInfo", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcWebsocketGetInfoResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, + "/v1/websocketgetsubscriptions": { + "get": { + "operationId": "WebsocketGetSubscriptions", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcWebsocketGetSubscriptionsResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, + "/v1/websocketsetenabled": { + "get": { + "operationId": "WebsocketSetEnabled", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGenericResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "enable", + "in": "query", + "required": false, + "type": "boolean", + "format": "boolean" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, + "/v1/websocketsetproxy": { + "get": { + "operationId": "WebsocketSetProxy", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGenericResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "proxy", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, + "/v1/websocketseturl": { + "get": { + "operationId": "WebsocketSetURL", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGenericResponse" + } + }, + "default": { + "description": "An unexpected error response", + "schema": { + "$ref": "#/definitions/runtimeError" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "url", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "GoCryptoTrader" + ] + } + }, "/v1/whalebomb": { "post": { - "operationId": "GoCryptoTrader_WhaleBomb", + "operationId": "WhaleBomb", "responses": { "200": { "description": "A successful response.", @@ -1809,7 +2086,7 @@ }, "/v1/withdrawaleventbydate": { "post": { - "operationId": "GoCryptoTrader_WithdrawalEventsByDate", + "operationId": "WithdrawalEventsByDate", "responses": { "200": { "description": "A successful response.", @@ -1841,7 +2118,7 @@ }, "/v1/withdrawaleventbyid": { "post": { - "operationId": "GoCryptoTrader_WithdrawalEventsByExchange", + "operationId": "WithdrawalEventsByExchange", "responses": { "200": { "description": "A successful response.", @@ -1873,7 +2150,7 @@ }, "/v1/withdrawfiatfunds": { "post": { - "operationId": "GoCryptoTrader_WithdrawFiatFunds", + "operationId": "WithdrawFiatFunds", "responses": { "200": { "description": "A successful response.", @@ -1905,7 +2182,7 @@ }, "/v1/withdrawithdrawcryptofundswfiatfunds": { "post": { - "operationId": "GoCryptoTrader_WithdrawCryptocurrencyFunds", + "operationId": "WithdrawCryptocurrencyFunds", "responses": { "200": { "description": "A successful response.", @@ -2038,9 +2315,6 @@ } } }, - "gctrpcAddPortfolioAddressResponse": { - "type": "object" - }, "gctrpcAuditEvent": { "type": "object", "properties": { @@ -2103,9 +2377,6 @@ } } }, - "gctrpcCancelOrderResponse": { - "type": "object" - }, "gctrpcCandle": { "type": "object", "properties": { @@ -2220,20 +2491,6 @@ } } }, - "gctrpcExchangePairRequest": { - "type": "object", - "properties": { - "exchange": { - "type": "string" - }, - "asset_type": { - "type": "string" - }, - "pair": { - "$ref": "#/definitions/gctrpcCurrencyPair" - } - } - }, "gctrpcFiatWithdrawalEvent": { "type": "object", "properties": { @@ -2335,17 +2592,6 @@ } } }, - "gctrpcGCTScriptGenericResponse": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "data": { - "type": "string" - } - } - }, "gctrpcGCTScriptListAllRequest": { "type": "object" }, @@ -2427,11 +2673,16 @@ } } }, - "gctrpcGenericExchangeNameResponse": { - "type": "object" - }, - "gctrpcGenericSubsystemResponse": { - "type": "object" + "gctrpcGenericResponse": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "data": { + "type": "string" + } + } }, "gctrpcGetAccountInfoResponse": { "type": "object", @@ -2544,6 +2795,14 @@ } } }, + "gctrpcGetExchangeAssetsResponse": { + "type": "object", + "properties": { + "assets": { + "type": "string" + } + } + }, "gctrpcGetExchangeInfoResponse": { "type": "object", "properties": { @@ -3113,9 +3372,6 @@ } } }, - "gctrpcRemoveEventResponse": { - "type": "object" - }, "gctrpcRemovePortfolioAddressRequest": { "type": "object", "properties": { @@ -3130,8 +3386,26 @@ } } }, - "gctrpcRemovePortfolioAddressResponse": { - "type": "object" + "gctrpcSetExchangePairRequest": { + "type": "object", + "properties": { + "exchange": { + "type": "string" + }, + "asset_type": { + "type": "string" + }, + "pairs": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcCurrencyPair" + } + }, + "enable": { + "type": "boolean", + "format": "boolean" + } + } }, "gctrpcSetLoggerDetailsRequest": { "type": "object", @@ -3322,6 +3596,67 @@ } } }, + "gctrpcWebsocketGetInfoResponse": { + "type": "object", + "properties": { + "exchange": { + "type": "string" + }, + "supported": { + "type": "boolean", + "format": "boolean" + }, + "enabled": { + "type": "boolean", + "format": "boolean" + }, + "authenticated_supported": { + "type": "boolean", + "format": "boolean" + }, + "authenticated": { + "type": "boolean", + "format": "boolean" + }, + "running_url": { + "type": "string" + }, + "proxy_address": { + "type": "string" + } + } + }, + "gctrpcWebsocketGetSubscriptionsResponse": { + "type": "object", + "properties": { + "exchange": { + "type": "string" + }, + "subscriptions": { + "type": "array", + "items": { + "$ref": "#/definitions/gctrpcWebsocketSubscription" + } + } + } + }, + "gctrpcWebsocketSubscription": { + "type": "object", + "properties": { + "channel": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "asset": { + "type": "string" + }, + "params": { + "type": "string" + } + } + }, "gctrpcWhaleBombRequest": { "type": "object", "properties": { diff --git a/gctscript/README.md b/gctscript/README.md index 53ea2d41..e0e383cc 100644 --- a/gctscript/README.md +++ b/gctscript/README.md @@ -78,8 +78,8 @@ With an example configuration being: ``` - Start/Execute: ```shell script - gctcli gctscript execute - gctcli gctscript execute "timer.gct" "~/gctscript" + gctcli script execute + gctcli script execute "timer.gct" "~/gctscript" { "status": "ok", @@ -88,8 +88,8 @@ With an example configuration being: ``` - Stop: ```shell script - gctcli gctscript stop - gctcli gctscript stop 821bd73e-02b1-4974-9463-874cb49f130d + gctcli script stop + gctcli script stop 821bd73e-02b1-4974-9463-874cb49f130d { "status": "ok", @@ -98,7 +98,7 @@ With an example configuration being: ``` - Status: ```shell script - gctcli gctscript status + gctcli script status { "status": "ok", @@ -113,8 +113,8 @@ With an example configuration being: ``` - Read file: ```shell script - gctcli gctscript read - gctcli gctscript read "timer.gct" + gctcli script read + gctcli script read "timer.gct" { "status": "ok", @@ -127,8 +127,8 @@ With an example configuration being: ``` - Query running script: ```shell script - gctcli gctscript query - gctcli gctscript query 821bd73e-02b1-4974-9463-874cb49f130d + gctcli script query + gctcli script query 821bd73e-02b1-4974-9463-874cb49f130d { "status": "ok", "script": { @@ -143,7 +143,7 @@ With an example configuration being: ``` - Add script to autoload: ```shell script - gctcli gctscript autoload add timer + gctcli script autoload add timer { "status": "success", "data": "script timer added to autoload list" @@ -151,7 +151,7 @@ With an example configuration being: ``` - Remove script from autoload: ```shell script - gctcli gctscript autoload remove timer + gctcli script autoload remove timer { "status": "success", "data": "script timer removed from autoload list" diff --git a/gctscript/modules/gct/exchange.go b/gctscript/modules/gct/exchange.go index 5b2d5d4d..c108f30c 100644 --- a/gctscript/modules/gct/exchange.go +++ b/gctscript/modules/gct/exchange.go @@ -54,10 +54,13 @@ func ExchangeOrderbook(args ...objects.Object) (objects.Object, error) { return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam) } - pairs := currency.NewPairDelimiter(currencyPair, delimiter) + pair, err := currency.NewPairDelimiter(currencyPair, delimiter) + if err != nil { + return nil, err + } assetType := asset.Item(assetTypeParam) - ob, err := wrappers.GetWrapper().Orderbook(exchangeName, pairs, assetType) + ob, err := wrappers.GetWrapper().Orderbook(exchangeName, pair, assetType) if err != nil { return nil, err } @@ -113,10 +116,14 @@ func ExchangeTicker(args ...objects.Object) (objects.Object, error) { return nil, fmt.Errorf(ErrParameterConvertFailed, assetTypeParam) } - pairs := currency.NewPairDelimiter(currencyPair, delimiter) + pair, err := currency.NewPairDelimiter(currencyPair, delimiter) + if err != nil { + return nil, err + } + assetType := asset.Item(assetTypeParam) - tx, err := wrappers.GetWrapper().Ticker(exchangeName, pairs, assetType) + tx, err := wrappers.GetWrapper().Ticker(exchangeName, pair, assetType) if err != nil { return nil, err } @@ -188,8 +195,9 @@ func ExchangePairs(args ...objects.Object) (objects.Object, error) { } r := objects.Array{} - for x := range rtnValue.Slice() { - r.Value = append(r.Value, &objects.String{Value: rtnValue.Slice()[x].String()}) + pairs := *(*[]currency.Pair)(rtnValue) + for x := range pairs { + r.Value = append(r.Value, &objects.String{Value: pairs[x].String()}) } return &r, nil } @@ -346,7 +354,10 @@ func ExchangeOrderSubmit(args ...objects.Object) (objects.Object, error) { if !ok { return nil, fmt.Errorf(ErrParameterConvertFailed, orderClientID) } - pair := currency.NewPairDelimiter(currencyPair, delimiter) + pair, err := currency.NewPairDelimiter(currencyPair, delimiter) + if err != nil { + return nil, err + } tempSubmit := &order.Submit{ Pair: pair, @@ -357,7 +368,7 @@ func ExchangeOrderSubmit(args ...objects.Object) (objects.Object, error) { ClientID: orderClientID, } - err := tempSubmit.Validate() + err = tempSubmit.Validate() if err != nil { return nil, err } @@ -550,10 +561,13 @@ func exchangeOHLCV(args ...objects.Object) (objects.Object, error) { if err != nil { return nil, err } - pairs := currency.NewPairDelimiter(currencyPair, delimiter) + pair, err := currency.NewPairDelimiter(currencyPair, delimiter) + if err != nil { + return nil, err + } assetType := asset.Item(assetTypeParam) - ret, err := wrappers.GetWrapper().OHLCV(exchangeName, pairs, assetType, startTime, endTime, kline.Interval(interval)) + ret, err := wrappers.GetWrapper().OHLCV(exchangeName, pair, assetType, startTime, endTime, kline.Interval(interval)) if err != nil { return nil, err } diff --git a/gctscript/wrappers/gct/exchange/exchange.go b/gctscript/wrappers/gct/exchange/exchange.go index 8e8ac49d..b46996e6 100644 --- a/gctscript/wrappers/gct/exchange/exchange.go +++ b/gctscript/wrappers/gct/exchange/exchange.go @@ -70,10 +70,15 @@ func (e Exchange) Pairs(exch string, enabledOnly bool, item asset.Item) (*curren return nil, err } - if enabledOnly { - return &x.CurrencyPairs.Get(item).Enabled, nil + ps, err := x.CurrencyPairs.Get(item) + if err != nil { + return nil, err } - return &x.CurrencyPairs.Get(item).Available, nil + + if enabledOnly { + return &ps.Enabled, nil + } + return &ps.Available, nil } // QueryOrder returns details of a valid exchange order diff --git a/gctscript/wrappers/gct/exchange/exchange_test.go b/gctscript/wrappers/gct/exchange/exchange_test.go index c846b33c..16a4cb69 100644 --- a/gctscript/wrappers/gct/exchange/exchange_test.go +++ b/gctscript/wrappers/gct/exchange/exchange_test.go @@ -89,8 +89,11 @@ func TestExchange_IsEnabled(t *testing.T) { func TestExchange_Ticker(t *testing.T) { t.Parallel() - c := currency.NewPairDelimiter(pairs, delimiter) - _, err := exchangeTest.Ticker(exchName, c, assetType) + c, err := currency.NewPairDelimiter(pairs, delimiter) + if err != nil { + t.Fatal(err) + } + _, err = exchangeTest.Ticker(exchName, c, assetType) if err != nil { t.Fatal(err) } @@ -98,8 +101,11 @@ func TestExchange_Ticker(t *testing.T) { func TestExchange_Orderbook(t *testing.T) { t.Parallel() - c := currency.NewPairDelimiter(pairs, delimiter) - _, err := exchangeTest.Orderbook(exchName, c, assetType) + c, err := currency.NewPairDelimiter(pairs, delimiter) + if err != nil { + t.Fatal(err) + } + _, err = exchangeTest.Orderbook(exchName, c, assetType) if err != nil { t.Fatal(err) } @@ -141,8 +147,13 @@ func TestExchange_SubmitOrder(t *testing.T) { if !configureExchangeKeys() { t.Skip("no exchange configured test skipped") } + + c, err := currency.NewPairDelimiter(pairs, delimiter) + if err != nil { + t.Fatal(err) + } tempOrder := &order.Submit{ - Pair: currency.NewPairDelimiter(pairs, delimiter), + Pair: c, Type: orderType, Side: orderSide, TriggerPrice: 0, @@ -152,7 +163,7 @@ func TestExchange_SubmitOrder(t *testing.T) { ClientID: orderClientID, Exchange: exchName, } - _, err := exchangeTest.SubmitOrder(tempOrder) + _, err = exchangeTest.SubmitOrder(tempOrder) if err != nil { t.Fatal(err) } diff --git a/gctscript/wrappers/validator/validator.go b/gctscript/wrappers/validator/validator.go index c8f63fb8..228947b2 100644 --- a/gctscript/wrappers/validator/validator.go +++ b/gctscript/wrappers/validator/validator.go @@ -96,7 +96,12 @@ func (w Wrapper) Pairs(exch string, _ bool, _ asset.Item) (*currency.Pairs, erro return nil, errTestFailed } - pairs := currency.NewPairsFromStrings([]string{"btc_usd", "btc_aud", "btc_ltc"}) + pairs, err := currency.NewPairsFromStrings([]string{"btc_usd", + "btc_aud", + "btc_ltc"}) + if err != nil { + return nil, err + } return &pairs, nil } @@ -105,11 +110,17 @@ func (w Wrapper) QueryOrder(exch, _ string) (*order.Detail, error) { if exch == exchError.String() { return nil, errTestFailed } + + pair, err := currency.NewPairFromString("BTCAUD") + if err != nil { + return nil, err + } + return &order.Detail{ Exchange: exch, AccountID: "hello", ID: "1", - Pair: currency.NewPairFromString("BTCAUD"), + Pair: pair, Side: "ask", Type: "limit", Date: time.Now(), @@ -121,7 +132,6 @@ func (w Wrapper) QueryOrder(exch, _ string) (*order.Detail, error) { Fee: 0, Trades: []order.TradeHistory{ { - Timestamp: time.Now(), TID: "", Price: 1, Amount: 2, @@ -179,12 +189,11 @@ func (w Wrapper) AccountInformation(exch string) (account.Holdings, error) { { CurrencyName: currency.Code{ Item: ¤cy.Item{ - ID: 0, - FullName: "Bitcoin", - Symbol: "BTC", - Role: 1, - AssocChain: "", - AssocExchange: nil, + ID: 0, + FullName: "Bitcoin", + Symbol: "BTC", + Role: 1, + AssocChain: "", }, }, TotalValue: 100, diff --git a/gctscript/wrappers/validator/validator_test.go b/gctscript/wrappers/validator/validator_test.go index 679f22dc..eaa457d5 100644 --- a/gctscript/wrappers/validator/validator_test.go +++ b/gctscript/wrappers/validator/validator_test.go @@ -27,8 +27,8 @@ const ( ) var ( - currencyPair = currency.NewPairFromString("BTCAUD") - testWrapper = Wrapper{} + currencyPair, _ = currency.NewPairFromString("BTCAUD") + testWrapper = Wrapper{} ) func TestWrapper_Exchanges(t *testing.T) { @@ -101,8 +101,11 @@ func TestWrapper_DepositAddress(t *testing.T) { func TestWrapper_Orderbook(t *testing.T) { t.Parallel() - c := currency.NewPairDelimiter(pairs, delimiter) - _, err := testWrapper.Orderbook(exchName, c, assetType) + c, err := currency.NewPairDelimiter(pairs, delimiter) + if err != nil { + t.Fatal(err) + } + _, err = testWrapper.Orderbook(exchName, c, assetType) if err != nil { t.Fatal(err) } @@ -146,9 +149,12 @@ func TestWrapper_QueryOrder(t *testing.T) { func TestWrapper_SubmitOrder(t *testing.T) { t.Parallel() - + c, err := currency.NewPairDelimiter(pairs, delimiter) + if err != nil { + t.Fatal(err) + } tempOrder := &order.Submit{ - Pair: currency.NewPairDelimiter(pairs, delimiter), + Pair: c, Type: orderType, Side: orderSide, TriggerPrice: 0, @@ -158,7 +164,7 @@ func TestWrapper_SubmitOrder(t *testing.T) { ClientID: orderClientID, Exchange: "true", } - _, err := testWrapper.SubmitOrder(tempOrder) + _, err = testWrapper.SubmitOrder(tempOrder) if err != nil { t.Fatal(err) } @@ -171,8 +177,11 @@ func TestWrapper_SubmitOrder(t *testing.T) { func TestWrapper_Ticker(t *testing.T) { t.Parallel() - c := currency.NewPairDelimiter(pairs, delimiter) - _, err := testWrapper.Ticker(exchName, c, assetType) + c, err := currency.NewPairDelimiter(pairs, delimiter) + if err != nil { + t.Fatal(err) + } + _, err = testWrapper.Ticker(exchName, c, assetType) if err != nil { t.Fatal(err) } @@ -208,8 +217,11 @@ func TestWrapper_WithdrawalFiatFunds(t *testing.T) { } func TestWrapper_OHLCV(t *testing.T) { - c := currency.NewPairDelimiter(pairs, delimiter) - _, err := testWrapper.OHLCV("test", c, asset.Spot, time.Now().Add(-24*time.Hour), time.Now(), kline.OneDay) + c, err := currency.NewPairDelimiter(pairs, delimiter) + if err != nil { + t.Fatal(err) + } + _, err = testWrapper.OHLCV("test", c, asset.Spot, time.Now().Add(-24*time.Hour), time.Now(), kline.OneDay) if err != nil { t.Fatal(err) } diff --git a/go.mod b/go.mod index ae5b296e..ccbc52a2 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,12 @@ module github.com/thrasher-corp/gocryptotrader -go 1.12 +go 1.13 require ( + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/d5/tengo/v2 v2.6.0 + github.com/friendsofgo/errors v0.9.2 // indirect + github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/gofrs/uuid v3.3.0+incompatible github.com/golang/protobuf v1.4.2 github.com/google/go-querystring v1.0.0 @@ -14,8 +17,12 @@ require ( github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12 github.com/lib/pq v1.7.1 github.com/mattn/go-sqlite3 v1.14.0 + github.com/mitchellh/mapstructure v1.3.2 // indirect + github.com/pelletier/go-toml v1.8.0 // indirect github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.2.0 + github.com/spf13/afero v1.3.1 // indirect + github.com/spf13/cast v1.3.1 // indirect github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.5.1 // indirect github.com/thrasher-corp/gct-ta v0.0.0-20200623072738-f2b55b7f9f41 @@ -23,13 +30,17 @@ require ( github.com/thrasher-corp/sqlboiler v1.0.1-0.20191001234224-71e17f37a85e github.com/toorop/go-pusher v0.0.0-20180521062818-4521e2eb39fb github.com/urfave/cli v1.22.4 + github.com/volatiletech/inflect v0.0.1 // indirect github.com/volatiletech/null v8.0.0+incompatible + github.com/volatiletech/sqlboiler v3.7.1+incompatible // indirect golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e + golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect + golang.org/x/text v0.3.3 // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 google.golang.org/grpc v1.30.0 - google.golang.org/protobuf v1.25.0 + google.golang.org/protobuf v1.25.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.2.7 // indirect + gopkg.in/ini.v1 v1.57.0 // indirect ) diff --git a/go.sum b/go.sum index a19e2446..1561db47 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,8 @@ github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/d5/tengo/v2 v2.6.0 h1:D0cJtpiBzaLJ/Smv6nnUc/LIfO46oKwDx85NZtIRNRI= github.com/d5/tengo/v2 v2.6.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -67,8 +69,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ericlagergren/decimal v0.0.0-20180907214518-0bb163153a5d/go.mod h1:1yj25TwtUlJ+pfOu9apAVaM1RWfZGg+aFpd4hPQZekQ= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzjtgk= +github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -108,6 +114,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -152,6 +159,7 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -166,6 +174,7 @@ github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -174,8 +183,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= -github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.7.1 h1:FvD5XTVTDt+KON6oIoOmHq6B6HzGuYEhuTMpEG0yuBQ= github.com/lib/pq v1.7.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -196,6 +203,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -205,10 +214,13 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= +github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -248,8 +260,13 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.1 h1:GPTpEAuNr98px18yNQ66JllNil98wfRZ/5Ukny8FeQA= +github.com/spf13/afero v1.3.1/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -285,10 +302,14 @@ github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d h1:gI4/tqP6lCY5k6Sg+4k9qSoBXmPwG+xXgMpK7jivD4M= github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d/go.mod h1:jspfvgf53t5NLUT4o9L1IX0kIBNKamGq1tWc/MgWK9Q= +github.com/volatiletech/inflect v0.0.1 h1:2a6FcMQyhmPZcLa+uet3VJ8gLn/9svWhJxJYwvE8KsU= +github.com/volatiletech/inflect v0.0.1/go.mod h1:IBti31tG6phkHitLlr5j7shC5SOo//x0AjDzaJU1PLA= github.com/volatiletech/null v8.0.0+incompatible h1:7wP8m5d/gZ6kW/9GnrLtMCRre2dlEnaQ9Km5OXlK4zg= github.com/volatiletech/null v8.0.0+incompatible/go.mod h1:0wD98JzdqB+rLyZ70fN05VDbXbafIb0KU0MdVhCzmOQ= github.com/volatiletech/sqlboiler v3.5.0+incompatible h1:n160O7UQLpZVRnJY6VH5eRNkt7sQdQBZGCCZ3CUy1+g= github.com/volatiletech/sqlboiler v3.5.0+incompatible/go.mod h1:jLfDkkHWPbS2cWRLkyC20vQWaIQsASEY7gM7zSo11Yw= +github.com/volatiletech/sqlboiler v3.7.1+incompatible h1:dm9/NjDskQVwAarmpeZ2UqLn1NKE8M3WHSHBS4jw2x8= +github.com/volatiletech/sqlboiler v3.7.1+incompatible/go.mod h1:jLfDkkHWPbS2cWRLkyC20vQWaIQsASEY7gM7zSo11Yw= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -304,6 +325,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -370,12 +392,17 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -421,6 +448,7 @@ google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBr google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 h1:fiNLklpBwWK1mth30Hlwk+fcdBmIALlgF5iy77O37Ig= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -453,14 +481,17 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= +gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/testdata/configtest.json b/testdata/configtest.json index b452a883..95cc256c 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -507,8 +507,8 @@ ], "pairs": { "spot": { - "enabled": "BTCKRW,ETHKRW,DASHKRW,LTCKRW,ETCKRW,XRPKRW,BCHKRW,XMRKRW,ZECKRW,QTUMKRW,BTGKRW,EOSKRW", - "available": "BHPKRW,STEEMKRW,GTOKRW,ETCKRW,STRATKRW,FXKRW,LTCKRW,MIXKRW,THETAKRW,QTUMKRW,ADAKRW,MCOKRW,INSKRW,RDNKRW,CONKRW,FABKRW,ETHKRW,HDACKRW,BTCKRW,POWRKRW,CMTKRW,LBAKRW,ETHOSKRW,HCKRW,ETZKRW,PPTKRW,XVGKRW,WTCKRW,TMTGKRW,LOOMKRW,WETKRW,ABTKRW,ITCKRW,GXCKRW,ORBSKRW,ICXKRW,BSVKRW,MXCKRW,MITHKRW,ZECKRW,AEKRW,SALTKRW,ARNKRW,TRUEKRW,ENJKRW,GNTKRW,PLYKRW,XMRKRW,REPKRW,ZRXKRW,BTGKRW,APISKRW,QKCKRW,LRCKRW,DVPKRW,DADKRW,CHRKRW,BCHKRW,NPXSKRW,PIVXKRW,AMOKRW,RNTKRW,XEMKRW,FCTKRW,WOMKRW,WAXPKRW,DACKRW,OMGKRW,PCMKRW,CROKRW,FNBKRW,ANKRKRW,EOSKRW,KNCKRW,OCNKRW,MTLKRW,XSRKRW,VALORKRW,TRVKRW,AUTOKRW,HYCKRW,AOAKRW,BTTKRW,MBLKRW,VETKRW,XRPKRW,ZILKRW,ELFKRW,LAMBKRW,POLYKRW,IOSTKRW,BZNTKRW,DASHKRW,CTXCKRW,BATKRW,FZZKRW,PAYKRW,BCDKRW,SNTKRW,WAVESKRW,XLMKRW,LINKKRW,OGOKRW,WICCKRW,TRXKRW" + "enabled": "BTCKRW,ETHKRW,DASHKRW,LTCKRW,ETCKRW,XRPKRW,BCHKRW,ZECKRW,QTUMKRW,BTGKRW,EOSKRW", + "available": "BHPKRW,STEEMKRW,GTOKRW,ETCKRW,STRATKRW,FXKRW,LTCKRW,MIXKRW,THETAKRW,QTUMKRW,ADAKRW,MCOKRW,INSKRW,RDNKRW,CONKRW,FABKRW,ETHKRW,HDACKRW,BTCKRW,POWRKRW,CMTKRW,LBAKRW,ETHOSKRW,HCKRW,ETZKRW,PPTKRW,XVGKRW,WTCKRW,TMTGKRW,LOOMKRW,WETKRW,ABTKRW,ITCKRW,GXCKRW,ORBSKRW,ICXKRW,BSVKRW,MXCKRW,MITHKRW,ZECKRW,AEKRW,SALTKRW,ARNKRW,TRUEKRW,ENJKRW,GNTKRW,PLYKRW,REPKRW,ZRXKRW,BTGKRW,APISKRW,QKCKRW,LRCKRW,DVPKRW,DADKRW,CHRKRW,BCHKRW,NPXSKRW,PIVXKRW,AMOKRW,RNTKRW,XEMKRW,FCTKRW,WOMKRW,WAXPKRW,DACKRW,OMGKRW,PCMKRW,CROKRW,FNBKRW,ANKRKRW,EOSKRW,KNCKRW,OCNKRW,MTLKRW,XSRKRW,VALORKRW,TRVKRW,AUTOKRW,HYCKRW,AOAKRW,BTTKRW,MBLKRW,VETKRW,XRPKRW,ZILKRW,ELFKRW,LAMBKRW,POLYKRW,IOSTKRW,BZNTKRW,DASHKRW,CTXCKRW,BATKRW,FZZKRW,PAYKRW,BCDKRW,SNTKRW,WAVESKRW,XLMKRW,LINKKRW,OGOKRW,WICCKRW,TRXKRW" } } },