diff --git a/cmd/gctcli/helpers.go b/cmd/gctcli/helpers.go index dfe4567e..9d8eb398 100644 --- a/cmd/gctcli/helpers.go +++ b/cmd/gctcli/helpers.go @@ -10,6 +10,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/common" "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/timestamppb" ) func clearScreen() error { @@ -48,3 +49,8 @@ func negateLocalOffset(t time.Time) string { return t.In(loc).Format(common.SimpleTimeFormat) } + +func negateLocalOffsetTS(t time.Time) *timestamppb.Timestamp { + _, offset := time.Now().Zone() + return timestamppb.New(t.Add(time.Duration(-offset) * time.Second)) +} diff --git a/cmd/gctcli/main.go b/cmd/gctcli/main.go index 35997579..bc24a8d0 100644 --- a/cmd/gctcli/main.go +++ b/cmd/gctcli/main.go @@ -29,6 +29,7 @@ var ( certPath string timeout time.Duration exchangeCreds exchange.Credentials + verbose bool ) const defaultTimeout = time.Second * 30 @@ -60,6 +61,9 @@ func setupClient(c *cli.Context) (*grpc.ClientConn, context.CancelFunc, error) { flag, values := exchangeCreds.GetMetaData() c.Context = metadata.AppendToOutgoingContext(c.Context, flag, values) } + if verbose { + c.Context = metadata.AppendToOutgoingContext(c.Context, "verbose", "true") + } conn, err := grpc.DialContext(c.Context, host, opts...) return conn, cancel, err } @@ -137,6 +141,11 @@ func main() { Usage: "override config API One Time Password (OTP) for request", Destination: &exchangeCreds.OneTimePassword, }, + &cli.BoolFlag{ + Name: "verbose", + Usage: "allows the request to generate a more verbose outputs server side", + Destination: &verbose, + }, } app.Commands = []*cli.Command{ getInfoCommand, @@ -203,6 +212,7 @@ func main() { getFuturesPositionsCommand, getCollateralCommand, shutdownCommand, + technicalAnalysisCommand, } ctx, cancel := context.WithCancel(context.Background()) diff --git a/cmd/gctcli/technical_analysis.go b/cmd/gctcli/technical_analysis.go new file mode 100644 index 00000000..11dc66bf --- /dev/null +++ b/cmd/gctcli/technical_analysis.go @@ -0,0 +1,800 @@ +package main + +import ( + "errors" + "fmt" + "strconv" + "strings" + "time" + + "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/currency" + "github.com/thrasher-corp/gocryptotrader/gctrpc" + "github.com/urfave/cli/v2" +) + +var ( + taStartTime string + taEndTime string + taGranularity int64 + taPeriod int64 + taFastPeriod int64 + taSlowPeriod int64 + taMovingAverageType string + taStdDevUp float64 + taStdDevDown float64 +) + +var commonFlag = []cli.Flag{ + &cli.StringFlag{ + Name: "exchange", + Usage: "the exchange to act on", + }, + &cli.StringFlag{ + Name: "pair", + Usage: "currency pair", + }, + &cli.StringFlag{ + Name: "asset", + Usage: "asset", + }, + &cli.Int64Flag{ + Name: "granularity", + Aliases: []string{"g"}, + Usage: klineMessage, + Value: 86400, + Destination: &taGranularity, + }, + &cli.StringFlag{ + Name: "start", + Usage: "the start date", + Value: time.Now().AddDate(0, -1, 0).Format(common.SimpleTimeFormat), + Destination: &taStartTime, + }, + &cli.StringFlag{ + Name: "end", + Usage: "the end date", + Value: time.Now().Format(common.SimpleTimeFormat), + Destination: &taEndTime, + }, +} + +var ( + periodFlag = &cli.Int64Flag{ + Name: "period", + Usage: "denotes period (rolling window) for technical analysis", + Value: 9, + Destination: &taPeriod, + } + fastFlag = &cli.Int64Flag{ + Name: "fastperiod", + Usage: "denotes fast period (ema) for macd generation", + Value: 12, + Destination: &taFastPeriod, + } + slowFlag = &cli.Int64Flag{ + Name: "slowperiod", + Usage: "denotes slow period (ema) for macd generation", + Value: 26, + Destination: &taSlowPeriod, + } + stdDevUpFlag = &cli.Float64Flag{ + Name: "stddevup", + Usage: "standard deviation limit for upper band", + Value: 1.5, + Destination: &taStdDevUp, + } + stdDevDownFlag = &cli.Float64Flag{ + Name: "stddevdown", + Usage: "standard deviation limit for lower band", + Value: 1.5, + Destination: &taStdDevDown, + } + maTypeFlag = &cli.StringFlag{ + Name: "movingaveragetype", + Usage: "defines the moving average type for underlying calculation ('ema'/'sma')", + Value: "sma", + Destination: &taMovingAverageType, + } + + otherAssetFlag = []cli.Flag{ + &cli.StringFlag{ + Name: "comparisonexchange", + Usage: "the other exchange to compare to - if not supplied will default to initial exchange", + Aliases: []string{"ce", "cexchange", "oe", "otherexchange"}, + }, + &cli.StringFlag{ + Name: "comparisonpair", + Usage: "the other currency pair", + Aliases: []string{"cp", "cpair", "op", "otherpair"}, + }, + &cli.StringFlag{ + Name: "comparisonasset", + Usage: "the other asset - if not supplied will default to initial exchange", + Aliases: []string{"ca", "casset", "oa", "otherasset"}, + }, + } +) + +var technicalAnalysisCommand = &cli.Command{ + Name: "technicalanalysis", + Usage: "get techincal analysis command", + Aliases: []string{"ta"}, + ArgsUsage: " ", + Subcommands: []*cli.Command{ + { + Name: "twap", + Usage: "returns the time weighted average price", + ArgsUsage: " ", + Flags: commonFlag, + Action: getTWAP, + }, + { + Name: "vwap", + Usage: "returns the volume weighted average price", + ArgsUsage: " ", + Flags: commonFlag, + Action: getVWAP, + }, + { + Name: "atr", + Usage: "returns the average true range", + ArgsUsage: " ", + Flags: append(commonFlag, periodFlag), + Action: getATR, + }, + { + Name: "bbands", + Usage: "returns the bollinger bands", + ArgsUsage: " ", + Flags: append(commonFlag, periodFlag, stdDevUpFlag, stdDevDownFlag, maTypeFlag), + Action: getBollingerBands, + }, + { + Name: "coco", + Usage: "returns the correlation-coefficient", + ArgsUsage: " ", + Flags: append(commonFlag, append([]cli.Flag{periodFlag}, otherAssetFlag...)...), + Action: getCoco, + }, + { + Name: "sma", + Usage: "returns the simple moving average", + ArgsUsage: " ", + Flags: append(commonFlag, periodFlag), + Action: getSMA, + }, + { + Name: "ema", + Usage: "returns the exponential moving average", + ArgsUsage: " ", + Flags: append(commonFlag, periodFlag), + Action: getEMA, + }, + { + Name: "macd", + Usage: "returns the moving average convergence divergence", + ArgsUsage: " ", + Flags: append(commonFlag, periodFlag, fastFlag, slowFlag), + Action: getMACD, + }, + { + Name: "mfi", + Usage: "returns the money flow index", + ArgsUsage: " ", + Flags: append(commonFlag, periodFlag), + Action: getMFI, + }, + { + Name: "obv", + Usage: "returns the on balance volume", + ArgsUsage: " ", + Flags: commonFlag, + Action: getOBV, + }, + { + Name: "rsi", + Usage: "returns the relative strength index", + ArgsUsage: " ", + Flags: append(commonFlag, periodFlag), + Action: getRSI, + }, + }, +} + +func getTWAP(c *cli.Context) error { + return getTecnicalAnalysis(c, "TWAP") +} + +func getVWAP(c *cli.Context) error { + return getTecnicalAnalysis(c, "VWAP") +} + +func getATR(c *cli.Context) error { + return getTecnicalAnalysis(c, "ATR") +} + +func getSMA(c *cli.Context) error { + return getTecnicalAnalysis(c, "SMA") +} + +func getEMA(c *cli.Context) error { + return getTecnicalAnalysis(c, "EMA") +} + +func getMFI(c *cli.Context) error { + return getTecnicalAnalysis(c, "MFI") +} + +func getOBV(c *cli.Context) error { + return getTecnicalAnalysis(c, "OBV") +} + +func getRSI(c *cli.Context) error { + return getTecnicalAnalysis(c, "RSI") +} + +func getTecnicalAnalysis(c *cli.Context, algo string) 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() + } + + var cpString string + if c.IsSet("pair") { + cpString = c.String("pair") + } else { + cpString = c.Args().Get(1) + } + + pair, err := currency.NewPairFromString(cpString) + if err != nil { + return err + } + + var asset string + if c.IsSet("asset") { + asset = c.String("asset") + } else { + asset = c.Args().Get(2) + } + + asset = strings.ToLower(asset) + if !validAsset(asset) { + return errInvalidAsset + } + + if c.IsSet("granularity") { + taGranularity = c.Int64("granularity") + } else if c.Args().Get(3) != "" { + taGranularity, err = strconv.ParseInt(c.Args().Get(3), 10, 64) + if err != nil { + return err + } + } + + if !c.IsSet("start") { + if c.Args().Get(4) != "" { + taStartTime = c.Args().Get(4) + } + } else { + taStartTime, _ = c.Value("start").(string) + } + + if !c.IsSet("end") { + if c.Args().Get(5) != "" { + taEndTime = c.Args().Get(5) + } + } else { + taEndTime, _ = c.Value("end").(string) + } + + s, err := time.Parse(common.SimpleTimeFormat, taStartTime) + if err != nil { + return fmt.Errorf("invalid time format for start: %v", err) + } + e, err := time.Parse(common.SimpleTimeFormat, taEndTime) + if err != nil { + return fmt.Errorf("invalid time format for end: %v", err) + } + + err = common.StartEndTimeCheck(s, e) + if err != nil { + return err + } + + if !c.IsSet("period") { + if c.Args().Get(6) != "" { + taPeriod, err = strconv.ParseInt(c.Args().Get(6), 10, 64) + if err != nil { + return err + } + } + } else { + taPeriod, _ = c.Value("period").(int64) + } + + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + req := &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: exchange, + Pair: &gctrpc.CurrencyPair{ + Base: pair.Base.String(), + Quote: pair.Quote.String(), + }, + AssetType: asset, + AlgorithmType: algo, + Interval: taGranularity * int64(time.Second), + Start: negateLocalOffsetTS(s), + End: negateLocalOffsetTS(e), + Period: taPeriod, + } + + client := gctrpc.NewGoCryptoTraderServiceClient(conn) + result, err := client.GetTechnicalAnalysis(c.Context, req) + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +func getBollingerBands(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() + } + + var cpString string + if c.IsSet("pair") { + cpString = c.String("pair") + } else { + cpString = c.Args().Get(1) + } + + pair, err := currency.NewPairFromString(cpString) + if err != nil { + return err + } + + var asset string + if c.IsSet("asset") { + asset = c.String("asset") + } else { + asset = c.Args().Get(2) + } + + asset = strings.ToLower(asset) + if !validAsset(asset) { + return errInvalidAsset + } + + if c.IsSet("granularity") { + taGranularity = c.Int64("granularity") + } else if c.Args().Get(3) != "" { + taGranularity, err = strconv.ParseInt(c.Args().Get(3), 10, 64) + if err != nil { + return err + } + } + + if !c.IsSet("start") { + if c.Args().Get(4) != "" { + taStartTime = c.Args().Get(4) + } + } else { + taStartTime, _ = c.Value("start").(string) + } + + if !c.IsSet("end") { + if c.Args().Get(5) != "" { + taEndTime = c.Args().Get(5) + } + } else { + taEndTime, _ = c.Value("end").(string) + } + + s, err := time.Parse(common.SimpleTimeFormat, taStartTime) + if err != nil { + return fmt.Errorf("invalid time format for start: %v", err) + } + e, err := time.Parse(common.SimpleTimeFormat, taEndTime) + if err != nil { + return fmt.Errorf("invalid time format for end: %v", err) + } + + err = common.StartEndTimeCheck(s, e) + if err != nil { + return err + } + + if !c.IsSet("period") { + if c.Args().Get(6) != "" { + taPeriod, err = strconv.ParseInt(c.Args().Get(6), 10, 64) + if err != nil { + return err + } + } + } else { + taPeriod, _ = c.Value("period").(int64) + } + + if !c.IsSet("stddevup") { + if c.Args().Get(7) != "" { + taStdDevUp, err = strconv.ParseFloat(c.Args().Get(7), 64) + if err != nil { + return err + } + } + } else { + taStdDevUp, _ = c.Value("stddevup").(float64) + } + + if !c.IsSet("stddevdown") { + if c.Args().Get(8) != "" { + taStdDevDown, err = strconv.ParseFloat(c.Args().Get(8), 64) + if err != nil { + return err + } + } + } else { + taStdDevDown, _ = c.Value("stddevdown").(float64) + } + + if !c.IsSet("movingaveragetype") && c.Args().Get(9) != "" { + taMovingAverageType = c.Args().Get(9) + } else { + taMovingAverageType, _ = c.Value("movingaveragetype").(string) + } + + var maType int64 + switch strings.ToLower(taMovingAverageType) { + case "sma": + case "ema": + maType = 1 + default: + return errors.New("invalid moving average type") + } + + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + req := &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: exchange, + Pair: &gctrpc.CurrencyPair{ + Base: pair.Base.String(), + Quote: pair.Quote.String(), + }, + AssetType: asset, + AlgorithmType: "BBANDS", + Interval: taGranularity * int64(time.Second), + Start: negateLocalOffsetTS(s), + End: negateLocalOffsetTS(e), + Period: taPeriod, + StandardDeviationUp: taStdDevUp, + StandardDeviationDown: taStdDevDown, + MovingAverageType: maType, + } + + client := gctrpc.NewGoCryptoTraderServiceClient(conn) + result, err := client.GetTechnicalAnalysis(c.Context, req) + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +func getMACD(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() + } + + var cpString string + if c.IsSet("pair") { + cpString = c.String("pair") + } else { + cpString = c.Args().Get(1) + } + + pair, err := currency.NewPairFromString(cpString) + if err != nil { + return err + } + + var asset string + if c.IsSet("asset") { + asset = c.String("asset") + } else { + asset = c.Args().Get(2) + } + + asset = strings.ToLower(asset) + if !validAsset(asset) { + return errInvalidAsset + } + + if c.IsSet("granularity") { + taGranularity = c.Int64("granularity") + } else if c.Args().Get(3) != "" { + taGranularity, err = strconv.ParseInt(c.Args().Get(3), 10, 64) + if err != nil { + return err + } + } + + if !c.IsSet("start") { + if c.Args().Get(4) != "" { + taStartTime = c.Args().Get(4) + } + } else { + taStartTime, _ = c.Value("start").(string) + } + + if !c.IsSet("end") { + if c.Args().Get(5) != "" { + taEndTime = c.Args().Get(5) + } + } else { + taEndTime, _ = c.Value("end").(string) + } + + s, err := time.Parse(common.SimpleTimeFormat, taStartTime) + if err != nil { + return fmt.Errorf("invalid time format for start: %v", err) + } + e, err := time.Parse(common.SimpleTimeFormat, taEndTime) + if err != nil { + return fmt.Errorf("invalid time format for end: %v", err) + } + + err = common.StartEndTimeCheck(s, e) + if err != nil { + return err + } + + if !c.IsSet("period") { + if c.Args().Get(6) != "" { + taPeriod, err = strconv.ParseInt(c.Args().Get(6), 10, 64) + if err != nil { + return err + } + } + } else { + taPeriod, _ = c.Value("period").(int64) + } + + if !c.IsSet("fastperiod") { + if c.Args().Get(7) != "" { + taFastPeriod, err = strconv.ParseInt(c.Args().Get(7), 10, 64) + if err != nil { + return err + } + } + } else { + taFastPeriod, _ = c.Value("fastperiod").(int64) + } + + if !c.IsSet("slowperiod") { + if c.Args().Get(8) != "" { + taSlowPeriod, err = strconv.ParseInt(c.Args().Get(8), 10, 64) + if err != nil { + return err + } + } + } else { + taSlowPeriod, _ = c.Value("slowperiod").(int64) + } + + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + req := &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: exchange, + Pair: &gctrpc.CurrencyPair{ + Base: pair.Base.String(), + Quote: pair.Quote.String(), + }, + AssetType: asset, + AlgorithmType: "MACD", + Interval: taGranularity * int64(time.Second), + Start: negateLocalOffsetTS(s), + End: negateLocalOffsetTS(e), + Period: taPeriod, + SlowPeriod: taSlowPeriod, + FastPeriod: taFastPeriod, + } + + client := gctrpc.NewGoCryptoTraderServiceClient(conn) + result, err := client.GetTechnicalAnalysis(c.Context, req) + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +func getCoco(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() + } + + var cpString string + if c.IsSet("pair") { + cpString = c.String("pair") + } else { + cpString = c.Args().Get(1) + } + + pair, err := currency.NewPairFromString(cpString) + if err != nil { + return err + } + + var asset string + if c.IsSet("asset") { + asset = c.String("asset") + } else { + asset = c.Args().Get(2) + } + + asset = strings.ToLower(asset) + if !validAsset(asset) { + return errInvalidAsset + } + + if c.IsSet("granularity") { + taGranularity = c.Int64("granularity") + } else if c.Args().Get(3) != "" { + taGranularity, err = strconv.ParseInt(c.Args().Get(3), 10, 64) + if err != nil { + return err + } + } + + if !c.IsSet("start") { + if c.Args().Get(4) != "" { + taStartTime = c.Args().Get(4) + } + } else { + taStartTime, _ = c.Value("start").(string) + } + + if !c.IsSet("end") { + if c.Args().Get(5) != "" { + taEndTime = c.Args().Get(5) + } + } else { + taEndTime, _ = c.Value("end").(string) + } + + s, err := time.Parse(common.SimpleTimeFormat, taStartTime) + if err != nil { + return fmt.Errorf("invalid time format for start: %v", err) + } + e, err := time.Parse(common.SimpleTimeFormat, taEndTime) + if err != nil { + return fmt.Errorf("invalid time format for end: %v", err) + } + + err = common.StartEndTimeCheck(s, e) + if err != nil { + return err + } + + if !c.IsSet("period") { + if c.Args().Get(6) != "" { + taPeriod, err = strconv.ParseInt(c.Args().Get(6), 10, 64) + if err != nil { + return err + } + } + } else { + taPeriod, _ = c.Value("period").(int64) + } + + var otherExchange string + if c.IsSet("comparisonexchange") { + otherExchange = c.String("comparisonexchange") + } else { + otherExchange = c.Args().Get(7) + } + + var oCpString string + if c.IsSet("comparisonpair") { + oCpString = c.String("comparisonpair") + } else { + oCpString = c.Args().Get(8) + } + + if oCpString == "" { + return errors.New("other pair is empty, to compare this must be specified") + } + otherPair, err := currency.NewPairFromString(oCpString) + if err != nil { + return err + } + + var otherAsset string + if c.IsSet("comparisonasset") { + otherAsset = c.String("comparisonasset") + } else { + otherAsset = c.Args().Get(9) + } + + otherAsset = strings.ToLower(otherAsset) + if otherAsset != "" && !validAsset(otherAsset) { + return errInvalidAsset + } + + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + req := &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: exchange, + Pair: &gctrpc.CurrencyPair{ + Base: pair.Base.String(), + Quote: pair.Quote.String(), + }, + AssetType: asset, + AlgorithmType: "COCO", + Interval: taGranularity * int64(time.Second), + Start: negateLocalOffsetTS(s), + End: negateLocalOffsetTS(e), + Period: taPeriod, + OtherExchange: otherExchange, + OtherPair: &gctrpc.CurrencyPair{Base: otherPair.Base.String(), Quote: otherPair.Quote.String()}, + OtherAssetType: otherAsset, + } + + client := gctrpc.NewGoCryptoTraderServiceClient(conn) + result, err := client.GetTechnicalAnalysis(c.Context, req) + if err != nil { + return err + } + + jsonOutput(result) + return nil +} diff --git a/engine/rpcserver.go b/engine/rpcserver.go index 66ca027c..1aa9dedf 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -19,6 +19,7 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/pquerna/otp/totp" "github.com/shopspring/decimal" + "github.com/thrasher-corp/gct-ta/indicators" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/common/file" @@ -36,6 +37,7 @@ 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/request" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" "github.com/thrasher-corp/gocryptotrader/exchanges/trade" "github.com/thrasher-corp/gocryptotrader/gctrpc" @@ -71,6 +73,7 @@ var ( errNoAccountInformation = errors.New("account information does not exist") errShutdownNotAllowed = errors.New("shutting down this bot instance is not allowed via gRPC, please enable by command line flag --grpcshutdown or config.json field grpcAllowBotShutdown") errGRPCShutdownSignalIsNil = errors.New("cannot shutdown, gRPC shutdown channel is nil") + errInvalidStrategy = errors.New("invalid strategy") ) // RPCServer struct @@ -108,7 +111,15 @@ func (s *RPCServer) authenticateClient(ctx context.Context) (context.Context, er password != s.Config.RemoteControl.Password { return ctx, fmt.Errorf("username/password mismatch") } - return exchange.ParseCredentialsMetadata(ctx, md) + ctx, err = exchange.ParseCredentialsMetadata(ctx, md) + if err != nil { + return ctx, err + } + + if _, ok := md["verbose"]; ok { + ctx = request.WithVerbose(ctx) + } + return ctx, nil } // StartRPCServer starts a gRPC server with TLS auth @@ -4575,3 +4586,166 @@ func (s *RPCServer) Shutdown(_ context.Context, _ *gctrpc.ShutdownRequest) (*gct s.Engine.GRPCShutdownSignal = nil return &gctrpc.ShutdownResponse{}, nil } + +// GetTechnicalAnalysis using the requested technical analysis method will +// return a set(s) of signals for price action analysis. +func (s *RPCServer) GetTechnicalAnalysis(ctx context.Context, r *gctrpc.GetTechnicalAnalysisRequest) (*gctrpc.GetTechnicalAnalysisResponse, error) { + exch, err := s.GetExchangeByName(r.Exchange) + if err != nil { + return nil, err + } + + as, err := asset.New(r.AssetType) + if err != nil { + return nil, err + } + + pair, err := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote) + if err != nil { + return nil, err + } + + klineInterval := kline.Interval(r.Interval) + + err = exch.GetBase().ValidateKline(pair, as, klineInterval) + if err != nil { + return nil, err + } + + klines, err := exch.GetHistoricCandlesExtended(ctx, + pair, + as, + r.Start.AsTime(), + r.End.AsTime(), + klineInterval) + if err != nil { + return nil, err + } + + signals := make(map[string]*gctrpc.ListOfSignals) + switch strings.ToUpper(r.AlgorithmType) { + case "TWAP": + var price float64 + price, err = klines.GetTWAP() + if err != nil { + return nil, err + } + signals["TWAP"] = &gctrpc.ListOfSignals{Signals: []float64{price}} + case "VWAP": + var prices []float64 + prices, err = klines.GetVWAPs() + if err != nil { + return nil, err + } + signals["VWAP"] = &gctrpc.ListOfSignals{Signals: prices} + case "ATR": + var prices []float64 + prices, err = klines.GetAverageTrueRange(r.Period) + if err != nil { + return nil, err + } + signals["ATR"] = &gctrpc.ListOfSignals{Signals: prices} + case "BBANDS": + var bollinger *kline.Bollinger + bollinger, err = klines.GetBollingerBands(r.Period, + r.StandardDeviationUp, + r.StandardDeviationDown, + indicators.MaType(r.MovingAverageType)) + if err != nil { + return nil, err + } + signals["UPPER"] = &gctrpc.ListOfSignals{Signals: bollinger.Upper} + signals["MIDDLE"] = &gctrpc.ListOfSignals{Signals: bollinger.Middle} + signals["LOWER"] = &gctrpc.ListOfSignals{Signals: bollinger.Lower} + case "COCO": + otherExch := exch + if r.OtherExchange != "" { + otherExch, err = s.GetExchangeByName(r.OtherExchange) + if err != nil { + return nil, err + } + } + + otherAs := as + if r.OtherAssetType != "" { + otherAs, err = asset.New(r.OtherAssetType) + if err != nil { + return nil, err + } + } + + if r.OtherPair.String() == "" { + return nil, errors.New("other pair is empty, to compare this must be specified") + } + + var otherPair currency.Pair + otherPair, err = currency.NewPairFromStrings(r.OtherPair.Base, r.OtherPair.Quote) + if err != nil { + return nil, err + } + + var otherKlines kline.Item + otherKlines, err = otherExch.GetHistoricCandlesExtended(ctx, + otherPair, otherAs, r.Start.AsTime(), r.End.AsTime(), klineInterval) + if err != nil { + return nil, err + } + + var correlation []float64 + correlation, err = klines.GetCorrelationCoefficient(&otherKlines, r.Period) + if err != nil { + return nil, err + } + signals["COCO"] = &gctrpc.ListOfSignals{Signals: correlation} + case "SMA": + var prices []float64 + prices, err = klines.GetSimpleMovingAverageOnClose(r.Period) + if err != nil { + return nil, err + } + signals["SMA"] = &gctrpc.ListOfSignals{Signals: prices} + case "EMA": + var prices []float64 + prices, err = klines.GetExponentialMovingAverageOnClose(r.Period) + if err != nil { + return nil, err + } + signals["EMA"] = &gctrpc.ListOfSignals{Signals: prices} + case "MACD": + var macd *kline.MACD + macd, err = klines.GetMovingAverageConvergenceDivergenceOnClose(r.FastPeriod, + r.SlowPeriod, + r.Period) + if err != nil { + return nil, err + } + signals["MACD"] = &gctrpc.ListOfSignals{Signals: macd.Results} + signals["SIGNAL"] = &gctrpc.ListOfSignals{Signals: macd.SignalVals} + signals["HISTOGRAM"] = &gctrpc.ListOfSignals{Signals: macd.Histogram} + case "MFI": + var prices []float64 + prices, err = klines.GetMoneyFlowIndex(r.Period) + if err != nil { + return nil, err + } + signals["MFI"] = &gctrpc.ListOfSignals{Signals: prices} + case "OBV": + var prices []float64 + prices, err = klines.GetOnBalanceVolume() + if err != nil { + return nil, err + } + signals["OBV"] = &gctrpc.ListOfSignals{Signals: prices} + case "RSI": + var prices []float64 + prices, err = klines.GetRelativeStrengthIndexOnClose(r.Period) + if err != nil { + return nil, err + } + signals["RSI"] = &gctrpc.ListOfSignals{Signals: prices} + default: + return nil, fmt.Errorf("%w '%s'", errInvalidStrategy, r.AlgorithmType) + } + + return &gctrpc.GetTechnicalAnalysisResponse{Signals: signals}, nil +} diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index fd68b04c..d2045b23 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -72,22 +72,29 @@ func (f fExchange) GetHistoricCandles(ctx context.Context, p currency.Pair, a as }, nil } +func generateCandles(amount int, timeStart time.Time, interval kline.Interval) []kline.Candle { + candy := make([]kline.Candle, amount) + for x := 0; x < amount; x++ { + candy[x] = kline.Candle{ + Time: timeStart, + Open: 1337, + High: 1337, + Low: 1337, + Close: 1337, + Volume: 1337, + } + timeStart = timeStart.Add(interval.Duration()) + } + return candy +} + func (f fExchange) GetHistoricCandlesExtended(ctx context.Context, p currency.Pair, a asset.Item, timeStart, _ time.Time, interval kline.Interval) (kline.Item, error) { return kline.Item{ Exchange: fakeExchangeName, Pair: p, Asset: a, Interval: interval, - Candles: []kline.Candle{ - { - Time: timeStart, - Open: 1337, - High: 1337, - Low: 1337, - Close: 1337, - Volume: 1337, - }, - }, + Candles: generateCandles(33, timeStart, interval), }, nil } @@ -2340,3 +2347,275 @@ func TestShutdown(t *testing.T) { t.Fatalf("received: '%v' but expected: '%v'", err, nil) } } + +func TestGetTechnicalAnalysis(t *testing.T) { + t.Parallel() + + em := SetupExchangeManager() + exch, err := em.NewExchangeByName(testExchange) + if err != nil { + t.Fatal(err) + } + b := exch.GetBase() + b.Name = fakeExchangeName + b.Enabled = true + + cp, err := currency.NewPairFromString("btc-usd") + if !errors.Is(err, nil) { + t.Fatalf("received '%v', expected '%v'", err, nil) + } + + b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) + b.CurrencyPairs.Pairs[asset.Futures] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, + } + b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{ + AssetEnabled: convert.BoolPtr(true), + ConfigFormat: ¤cy.PairFormat{}, + Available: currency.Pairs{cp}, + Enabled: currency.Pairs{cp}, + } + + b.Features.Enabled.Kline.Intervals = map[string]bool{ + kline.OneDay.Word(): true, + } + em.Add(fExchange{IBotExchange: exch}) + s := RPCServer{ + Engine: &Engine{ + ExchangeManager: em, + currencyStateManager: &CurrencyStateManager{ + started: 1, + iExchangeManager: em, + }, + }, + } + + _, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{}) + if !errors.Is(err, ErrExchangeNameIsEmpty) { + t.Fatalf("received: '%v' but expected: '%v'", err, ErrExchangeNameIsEmpty) + } + + _, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + }) + if !errors.Is(err, asset.ErrNotSupported) { + t.Fatalf("received: '%v' but expected: '%v'", err, asset.ErrNotSupported) + } + + _, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "upsideprofitcontract", + Pair: &gctrpc.CurrencyPair{}, + }) + if !errors.Is(err, kline.ErrValidatingParams) { + t.Fatalf("received: '%v' but expected: '%v'", err, kline.ErrValidatingParams) + } + + _, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + }) + if !errors.Is(err, errInvalidStrategy) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidStrategy) + } + + resp, err := s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "twap", + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if resp.Signals["TWAP"].Signals[0] != 1337 { + t.Fatalf("received: '%v' but expected: '%v'", resp.Signals["TWAP"].Signals[0], 1337) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "vwap", + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["VWAP"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["VWAP"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "atr", + Period: 9, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["ATR"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["ATR"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "bbands", + Period: 9, + StandardDeviationUp: 0.5, + StandardDeviationDown: 0.5, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["UPPER"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["UPPER"].Signals), 33) + } + + if len(resp.Signals["MIDDLE"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["MIDDLE"].Signals), 33) + } + + if len(resp.Signals["LOWER"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["LOWER"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + OtherPair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "COCO", + Period: 9, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["COCO"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["COCO"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "sma", + Period: 9, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["SMA"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["SMA"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "ema", + Period: 9, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["EMA"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["EMA"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "macd", + Period: 9, + FastPeriod: 12, + SlowPeriod: 26, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["MACD"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["MACD"].Signals), 33) + } + + if len(resp.Signals["SIGNAL"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["SIGNAL"].Signals), 33) + } + + if len(resp.Signals["HISTOGRAM"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["HISTOGRAM"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "mfi", + Period: 9, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["MFI"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["MFI"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "obv", + Period: 9, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["OBV"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["OBV"].Signals), 33) + } + + resp, err = s.GetTechnicalAnalysis(context.Background(), &gctrpc.GetTechnicalAnalysisRequest{ + Exchange: fakeExchangeName, + AssetType: "spot", + Pair: &gctrpc.CurrencyPair{Base: "btc", Quote: "usd"}, + Interval: int64(kline.OneDay), + AlgorithmType: "rsi", + Period: 9, + }) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if len(resp.Signals["RSI"].Signals) != 33 { + t.Fatalf("received: '%v' but expected: '%v'", len(resp.Signals["RSI"].Signals), 33) + } +} diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 882bfec4..e010e359 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -114,9 +114,15 @@ func (b *BTCMarkets) SetDefaults() { AutoPairUpdates: true, Kline: kline.ExchangeCapabilitiesEnabled{ Intervals: map[string]bool{ - kline.OneMin.Word(): true, - kline.OneHour.Word(): true, - kline.OneDay.Word(): true, + kline.OneMin.Word(): true, + kline.FiveMin.Word(): true, + kline.FifteenMin.Word(): true, + kline.ThirtyMin.Word(): true, + kline.OneHour.Word(): true, + kline.SixHour.Word(): true, + kline.OneDay.Word(): true, + kline.OneWeek.Word(): true, + kline.OneMonth.Word(): true, }, ResultLimit: 1000, }, @@ -978,8 +984,25 @@ func (b *BTCMarkets) ValidateCredentials(ctx context.Context, assetType asset.It // FormatExchangeKlineInterval returns Interval to exchange formatted string func (b *BTCMarkets) FormatExchangeKlineInterval(in kline.Interval) string { - if in == kline.OneDay { + switch in { + case kline.OneMin: + return "1m" + case kline.FiveMin: + return "5m" + case kline.FifteenMin: + return "15m" + case kline.ThirtyMin: + return "30m" + case kline.OneHour: + return "1h" + case kline.SixHour: + return "6h" + case kline.OneDay: return "1d" + case kline.OneWeek: + return "1w" + case kline.OneMonth: + return "1mo" } return in.Short() } diff --git a/exchanges/exchange.go b/exchanges/exchange.go index d3f0c88c..1c3c8dc3 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -988,26 +988,22 @@ func (b *Base) FormatExchangeKlineInterval(in kline.Interval) string { return strconv.FormatFloat(in.Duration().Seconds(), 'f', 0, 64) } -// ValidateKline confirms that the requested pair, asset & interval are supported and/or enabled by the requested exchange +// ValidateKline confirms that the requested pair, asset & interval are +// supported and/or enabled by the requested exchange. func (b *Base) ValidateKline(pair currency.Pair, a asset.Item, interval kline.Interval) error { var errorList []string - var err kline.Error if b.CurrencyPairs.IsAssetEnabled(a) != nil { - err.Asset = a - errorList = append(errorList, "asset not enabled") + errorList = append(errorList, fmt.Sprintf("[%s] asset not enabled", a)) } else if !b.CurrencyPairs.Pairs[a].Enabled.Contains(pair, true) { - err.Pair = pair - errorList = append(errorList, "pair not enabled") + errorList = append(errorList, fmt.Sprintf("[%s] pair not enabled", pair)) } if !b.klineIntervalEnabled(interval) { - err.Interval = interval - errorList = append(errorList, "interval not supported") + errorList = append(errorList, fmt.Sprintf("[%s] interval not supported", interval)) } if len(errorList) > 0 { - err.Err = errors.New(strings.Join(errorList, ",")) - return &err + return fmt.Errorf("%w: %v", kline.ErrValidatingParams, strings.Join(errorList, ", ")) } return nil diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 731bac8c..326e2416 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -37,7 +37,7 @@ const ( var ( h HUOBI wsSetupRan bool - futuresTestPair = currency.NewPair(currency.BTC, currency.NewCode("NQ")) + futuresTestPair = currency.NewPair(currency.BTC, currency.NewCode("CW")) // represents this week - NQ (next quarter) is erroring out. ) func TestMain(m *testing.M) { @@ -138,7 +138,7 @@ func TestFIndexPriceInfo(t *testing.T) { func TestFContractPriceLimitations(t *testing.T) { t.Parallel() _, err := h.FContractPriceLimitations(context.Background(), - "BTC", "next_quarter", currency.EMPTYPAIR) + "BTC", "this_week", currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -147,7 +147,7 @@ func TestFContractPriceLimitations(t *testing.T) { func TestFContractOpenInterest(t *testing.T) { t.Parallel() _, err := h.FContractOpenInterest(context.Background(), - "BTC", "next_quarter", currency.EMPTYPAIR) + "BTC", "this_week", currency.EMPTYPAIR) if err != nil { t.Error(err) } @@ -231,7 +231,7 @@ func TestFQueryTieredAdjustmentFactor(t *testing.T) { func TestFQueryHisOpenInterest(t *testing.T) { t.Parallel() _, err := h.FQueryHisOpenInterest(context.Background(), - "BTC", "next_quarter", "60min", "cont", 3) + "BTC", "this_week", "60min", "cont", 3) if err != nil { t.Error(err) } @@ -2732,8 +2732,8 @@ func TestFormatFuturesPair(t *testing.T) { if err != nil { t.Fatal(err) } - if r != "BTC_NQ" { - t.Errorf("expected BTC_NQ, got %s", r) + if r != "BTC_CW" { + t.Errorf("expected BTC_CW, got %s", r) } availInstruments, err := h.FetchTradablePairs(context.Background(), asset.Futures) if err != nil { diff --git a/exchanges/kline/kline.go b/exchanges/kline/kline.go index 23ac8141..9b19840a 100644 --- a/exchanges/kline/kline.go +++ b/exchanges/kline/kline.go @@ -222,7 +222,7 @@ func (k *Item) SortCandlesByTimestamp(desc bool) { }) } -// FormatDates converts all date to UTC time +// FormatDates converts all dates to UTC time func (k *Item) FormatDates() { for x := range k.Candles { k.Candles[x].Time = k.Candles[x].Time.UTC() @@ -324,13 +324,15 @@ func TotalCandlesPerInterval(start, end time.Time, interval Interval) (out float return -1 } +var oneYearDurationInNano = float64(OneYear.Duration().Nanoseconds()) + // IntervalsPerYear helps determine the number of intervals in a year // used in CAGR calculation to know the amount of time of an interval in a year func (i *Interval) IntervalsPerYear() float64 { if i.Duration() == 0 { return 0 } - return float64(OneYear.Duration().Nanoseconds()) / float64(i.Duration().Nanoseconds()) + return oneYearDurationInNano / float64(i.Duration().Nanoseconds()) } // ConvertToNewInterval allows the scaling of candles to larger candles @@ -557,10 +559,7 @@ func (h *IntervalRangeHolder) createDateSummaryRange(start, end time.Time, hasDa // CreateIntervalTime is a simple helper function to set the time twice func CreateIntervalTime(tt time.Time) IntervalTime { - return IntervalTime{ - Time: tt, - Ticks: tt.Unix(), - } + return IntervalTime{Time: tt, Ticks: tt.Unix()} } // Equal allows for easier unix comparison diff --git a/exchanges/kline/kline_test.go b/exchanges/kline/kline_test.go index 88b1b3bb..5a0d36ed 100644 --- a/exchanges/kline/kline_test.go +++ b/exchanges/kline/kline_test.go @@ -262,31 +262,6 @@ func TestDurationToWord(t *testing.T) { } } -func TestKlineErrors(t *testing.T) { - t.Parallel() - v := Error{ - Interval: OneYear, - Pair: currency.NewPair(currency.BTC, currency.AUD), - Err: errors.New("hello world"), - } - - if v.Interval != OneYear { - t.Fatalf("expected OneYear received %v:", v.Interval) - } - - if v.Pair != currency.NewPair(currency.BTC, currency.AUD) { - t.Fatalf("expected OneYear received %v:", v.Pair) - } - - if v.Error() != "hello world" { - t.Fatal("expected error return received empty value") - } - - if v.Unwrap().Error() != "hello world" { - t.Fatal("expected error return received empty value") - } -} - func TestTotalCandlesPerInterval(t *testing.T) { t.Parallel() start := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC) diff --git a/exchanges/kline/kline_types.go b/exchanges/kline/kline_types.go index 35ab1c61..5225935a 100644 --- a/exchanges/kline/kline_types.go +++ b/exchanges/kline/kline_types.go @@ -52,6 +52,10 @@ var ( // ErrNotFoundAtTime returned when looking up a candle at a specific time ErrNotFoundAtTime = errors.New("candle not found at time") + // ErrValidatingParams defines an error when the kline params are either not + // enabled or are invalid. + ErrValidatingParams = errors.New("kline param(s) are invalid") + // SupportedIntervals is a list of all supported intervals SupportedIntervals = []Interval{ FifteenSecond, @@ -131,24 +135,6 @@ type ExchangeCapabilitiesEnabled struct { // Interval type for kline Interval usage type Interval time.Duration -// Error struct to hold kline interval errors -type Error struct { - Asset asset.Item - Pair currency.Pair - Interval Interval - Err error -} - -// Error returns short interval unsupported message -func (e *Error) Error() string { - return e.Err.Error() -} - -// Unwrap returns interval unsupported message -func (e *Error) Unwrap() error { - return e.Err -} - // IntervalRangeHolder holds the entire range of intervals // and the start end dates of everything type IntervalRangeHolder struct { diff --git a/exchanges/kline/technical_analysis.go b/exchanges/kline/technical_analysis.go new file mode 100644 index 00000000..7fadf242 --- /dev/null +++ b/exchanges/kline/technical_analysis.go @@ -0,0 +1,354 @@ +package kline + +import ( + "errors" + "fmt" + + "github.com/thrasher-corp/gct-ta/indicators" +) + +var ( + errInvalidPeriod = errors.New("invalid period") + errNoData = errors.New("no data") + errInvalidDeviationMultiplier = errors.New("invalid deviation multiplier") + errNilOHLC = errors.New("nil OHLC data") + errInvalidDataSetLengths = errors.New("invalid data set lengths") + errNotEnoughData = errors.New("not enough data to derive signal") +) + +// OHLC is a connector for technical analysis usage +type OHLC struct { + Open []float64 + High []float64 + Low []float64 + Close []float64 + Volume []float64 +} + +// GetOHLC returns the entire subset of candles as a friendly type for gct +// technical analysis usage. +func (k *Item) GetOHLC() *OHLC { + ohlc := &OHLC{ + Open: make([]float64, len(k.Candles)), + High: make([]float64, len(k.Candles)), + Low: make([]float64, len(k.Candles)), + Close: make([]float64, len(k.Candles)), + Volume: make([]float64, len(k.Candles)), + } + for x := range k.Candles { + ohlc.Open[x] = k.Candles[x].Open + ohlc.High[x] = k.Candles[x].High + ohlc.Low[x] = k.Candles[x].Low + ohlc.Close[x] = k.Candles[x].Close + ohlc.Volume[x] = k.Candles[x].Volume + } + return ohlc +} + +// GetAverageTrueRange returns the Average True Range for the given period. +func (k *Item) GetAverageTrueRange(period int64) ([]float64, error) { + return k.GetOHLC().GetAverageTrueRange(period) +} + +// GetAverageTrueRange returns the Average True Range for the given period. +func (o *OHLC) GetAverageTrueRange(period int64) ([]float64, error) { + if o == nil { + return nil, fmt.Errorf("get average true range %w", errNilOHLC) + } + if period <= 0 { + return nil, fmt.Errorf("get average true range %w", errInvalidPeriod) + } + if len(o.High) == 0 { + return nil, fmt.Errorf("get average true range high %w", errNoData) + } + if len(o.Low) == 0 { + return nil, fmt.Errorf("get average true range low %w", errNoData) + } + if len(o.Close) == 0 { + return nil, fmt.Errorf("get average true range close %w", errNoData) + } + if int(period) > len(o.Close) { + return nil, fmt.Errorf("get average true range close %w exceeds data length, please reduce", + errInvalidPeriod) + } + return indicators.ATR(o.High, o.Low, o.Close, int(period)), nil +} + +// GetBollingerBands returns Bollinger Bands for the given period. +func (k *Item) GetBollingerBands(period int64, nbDevUp, nbDevDown float64, m indicators.MaType) (*Bollinger, error) { + return k.GetOHLC().GetBollingerBands(period, nbDevUp, nbDevDown, m) +} + +// Bollinger defines a return type for the bollinger bands +type Bollinger struct { + Upper []float64 + Middle []float64 + Lower []float64 +} + +// GetBollingerBands returns Bollinger Bands for the given period. +func (o *OHLC) GetBollingerBands(period int64, nbDevUp, nbDevDown float64, m indicators.MaType) (*Bollinger, error) { + if o == nil { + return nil, fmt.Errorf("get bollinger bands %w", errNilOHLC) + } + if period <= 0 { + return nil, fmt.Errorf("get bollinger bands %w", errInvalidPeriod) + } + if nbDevUp <= 0 { + return nil, fmt.Errorf("get bollinger bands %w upper limit", errInvalidDeviationMultiplier) + } + if nbDevDown <= 0 { + return nil, fmt.Errorf("get bollinger bands %w lower limit", errInvalidDeviationMultiplier) + } + if len(o.Close) == 0 { + return nil, fmt.Errorf("get bollinger bands close %w", errNoData) + } + if int(period) > len(o.Close) { // TODO: Investigate the panic when this protection is removed. + return nil, fmt.Errorf("get bollinger bands %w '%v' should not exceed close data length '%v'", + errInvalidPeriod, period, len(o.Close)) + } + var bands Bollinger + bands.Upper, bands.Middle, bands.Lower = indicators.BBANDS(o.Close, + int(period), + nbDevUp, + nbDevDown, + m) + return &bands, nil +} + +// GetCorrelationCoefficient returns GetCorrelation Coefficient against another +// candle data set for the given period. +func (k *Item) GetCorrelationCoefficient(other *Item, period int64) ([]float64, error) { + return k.GetOHLC().GetCorrelationCoefficient(other.GetOHLC(), period) +} + +// GetCorrelationCoefficient returns GetCorrelation Coefficient against another +// candle data set for the given period. +func (o *OHLC) GetCorrelationCoefficient(other *OHLC, period int64) ([]float64, error) { + if o == nil { + return nil, fmt.Errorf("get correlation coefficient %w", errNilOHLC) + } + if period <= 0 { + return nil, fmt.Errorf("get correlation coefficient %w", errInvalidPeriod) + } + if period == 1 { + // TODO: Check correlation calculation. + return nil, fmt.Errorf("get correlation coefficient %w using period 1 results in NaN return", + errInvalidPeriod) + } + if other == nil { + return nil, fmt.Errorf("get correlation coefficient %w", errNilOHLC) + } + + if len(o.Close) == 0 { + return nil, fmt.Errorf("get correlation coefficient close %w", errNoData) + } + if len(other.Close) == 0 { + return nil, fmt.Errorf("get correlation coefficient comparison close %w", errNoData) + } + if int(period) > len(o.Close) || int(period) > len(other.Close) { + return nil, fmt.Errorf("get correlation coefficient %w exceeds data length, please reduce", + errInvalidPeriod) + } + if len(o.Close) != len(other.Close) { + return nil, + fmt.Errorf("get correlation coefficient comparison close %w between data sets", + errInvalidDataSetLengths) + } + return indicators.CorrelationCoefficient(o.Close, other.Close, int(period)), nil +} + +// GetSimpleMovingAverageOnClose returns MA the close prices set for the given +// period. +func (k *Item) GetSimpleMovingAverageOnClose(period int64) ([]float64, error) { + ohlc := k.GetOHLC() + return ohlc.GetSimpleMovingAverage(ohlc.Close, period) +} + +// GetSimpleMovingAverage returns MA for the supplied price set for the given +// period. +func (o *OHLC) GetSimpleMovingAverage(option []float64, period int64) ([]float64, error) { + if o == nil { + return nil, fmt.Errorf("get simple moving average %w", errNilOHLC) + } + if period <= 0 { + return nil, fmt.Errorf("get simple moving average %w", errInvalidPeriod) + } + if len(option) == 0 { + return nil, fmt.Errorf("get simple moving average %w", errNoData) + } + if int(period) > len(option) { + return nil, fmt.Errorf("get simple moving average %w exceeds data length, please reduce", + errInvalidPeriod) + } + return indicators.SMA(option, int(period)), nil +} + +// GetExponentialMovingAverageOnClose returns the EMA on the close price set for +// the given period. +func (k *Item) GetExponentialMovingAverageOnClose(period int64) ([]float64, error) { + ohlc := k.GetOHLC() + return ohlc.GetExponentialMovingAverage(ohlc.Close, period) +} + +// GetExponentialMovingAverage returns the EMA on the supplied price set for the +// given period. +func (o *OHLC) GetExponentialMovingAverage(option []float64, period int64) ([]float64, error) { + if o == nil { + return nil, fmt.Errorf("get exponential moving average %w", errNilOHLC) + } + if period <= 0 { + return nil, fmt.Errorf("get exponential moving average %w", errInvalidPeriod) + } + if len(option) == 0 { + return nil, fmt.Errorf("get exponential moving average %w", errNoData) + } + if int(period) > len(option) { + return nil, fmt.Errorf("get exponential moving average %w exceeds data length, please reduce", + errInvalidPeriod) + } + return indicators.EMA(option, int(period)), nil +} + +// MACD defines MACD values +type MACD struct { + Results []float64 + SignalVals []float64 + Histogram []float64 +} + +// GetMovingAverageConvergenceDivergenceOnClose returns the +// MACD (macd, signal period vals, histogram) for the given price +// set and the parameters fast, slow signal time periods. +func (k *Item) GetMovingAverageConvergenceDivergenceOnClose(fast, slow, signal int64) (*MACD, error) { + ohlc := k.GetOHLC() + return ohlc.GetMovingAverageConvergenceDivergence(ohlc.Close, fast, slow, signal) +} + +// GetMovingAverageConvergenceDivergence returns the +// MACD (macd, signal period vals, histogram) for the given price +// set and the parameters fast, slow signal time periods. +func (o *OHLC) GetMovingAverageConvergenceDivergence(option []float64, fast, slow, signal int64) (*MACD, error) { + if o == nil { + return nil, fmt.Errorf("get macd %w", errNilOHLC) + } + if fast <= 0 { + return nil, fmt.Errorf("get macd %w fast", errInvalidPeriod) + } + if slow <= 0 { + return nil, fmt.Errorf("get macd %w slow", errInvalidPeriod) + } + if fast >= slow { + return nil, fmt.Errorf("get macd %w fast should not be equal or exceed slow", errInvalidPeriod) + } + if signal <= 0 { + return nil, fmt.Errorf("get macd %w signal", errInvalidPeriod) + } + if len(option) == 0 { + return nil, fmt.Errorf("get macd %w", errNoData) + } + + if len(option) < int(slow+signal-2) { + return nil, fmt.Errorf("get macd %w %v data points are less than minimum %v length requirement derived from the slow %v and signal %v period subtract two, increase end date or scale down granularity", + errNotEnoughData, + len(option), + slow+signal-2, + slow, + signal) + } + var macd MACD + macd.Results, macd.SignalVals, macd.Histogram = indicators.MACD(option, + int(fast), + int(slow), + int(signal)) + return &macd, nil +} + +// GetMoneyFlowIndex returns Money Flow Index for the given period. +func (k *Item) GetMoneyFlowIndex(period int64) ([]float64, error) { + return k.GetOHLC().GetMoneyFlowIndex(period) +} + +// GetMoneyFlowIndex returns Money Flow Index for the given period. +func (o *OHLC) GetMoneyFlowIndex(period int64) ([]float64, error) { + if o == nil { + return nil, fmt.Errorf("get money flow index %w", errNilOHLC) + } + if period <= 0 { + return nil, fmt.Errorf("get money flow index %w", errInvalidPeriod) + } + highLen := len(o.High) + if highLen == 0 { + return nil, fmt.Errorf("get money flow index %w for high", errNoData) + } + lowLen := len(o.Low) + if lowLen == 0 { + return nil, fmt.Errorf("get money flow index %w for low", errNoData) + } + closeLen := len(o.Close) + if closeLen == 0 { + return nil, fmt.Errorf("get money flow index %w for close", errNoData) + } + volLen := len(o.Volume) + if volLen == 0 { + return nil, fmt.Errorf("get money flow index %w for volume", errNoData) + } + if highLen != closeLen || lowLen != closeLen || volLen != closeLen { + // TODO: Investigate the panic when this protection is removed. + // This is very unstable with incorrect lengths. + return nil, fmt.Errorf("get money flow index %w", errInvalidDataSetLengths) + } + + if int(period) >= len(o.Close) { + // TODO: Investigate the panic when this protection is removed. + return nil, fmt.Errorf("get money flow index %w '%v' should not exceed or equal close data length '%v'", + errInvalidPeriod, period, len(o.Close)) + } + return indicators.MFI(o.High, o.Low, o.Close, o.Volume, int(period)), nil +} + +// GetOnBalanceVolume returns On Balance Volume. +func (k *Item) GetOnBalanceVolume() ([]float64, error) { + return k.GetOHLC().GetOnBalanceVolume() +} + +// GetOnBalanceVolume returns On Balance Volume. +func (o *OHLC) GetOnBalanceVolume() ([]float64, error) { + if o == nil { + return nil, fmt.Errorf("get on balance volume %w", errNilOHLC) + } + if len(o.Close) == 0 { + return nil, fmt.Errorf("get on balance volume %w for close", errNoData) + } + if len(o.Volume) == 0 { + return nil, fmt.Errorf("get on balance volume %w for volume", errNoData) + } + return indicators.OBV(o.Close, o.Volume), nil +} + +// GetRelativeStrengthIndexOnClose returns the relative strength index from the +// given price set and period. +func (k *Item) GetRelativeStrengthIndexOnClose(period int64) ([]float64, error) { + ohlc := k.GetOHLC() + return ohlc.GetRelativeStrengthIndex(ohlc.Close, period) +} + +// GetRelativeStrengthIndex returns the relative strength index from the the +// given price set and period. +func (o *OHLC) GetRelativeStrengthIndex(option []float64, period int64) ([]float64, error) { + if o == nil { + return nil, fmt.Errorf("get relative strength index %w", errNilOHLC) + } + if period <= 1 { + return nil, fmt.Errorf("get relative strength index %w cannot be equal or below 1", errInvalidPeriod) + } + if len(option) <= 2 { + // TODO: Check why 2 data points causes panic. + return nil, fmt.Errorf("get relative strength index %w, requires atleast 3 data points", errNotEnoughData) + } + if int(period) > len(option) { + return nil, fmt.Errorf("get exponential moving average %w exceeds data length, please reduce", + errInvalidPeriod) + } + return indicators.RSI(option, int(period)), nil +} diff --git a/exchanges/kline/technical_analysis_test.go b/exchanges/kline/technical_analysis_test.go new file mode 100644 index 00000000..09a30571 --- /dev/null +++ b/exchanges/kline/technical_analysis_test.go @@ -0,0 +1,429 @@ +package kline + +import ( + "errors" + "testing" +) + +func TestGetOHLC(t *testing.T) { + t.Parallel() + if (&Item{Candles: []Candle{{Open: 1337}}}).GetOHLC() == nil { + t.Fatal("unexpected value") + } +} + +func TestGetAverageTrueRange(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetAverageTrueRange(0) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetAverageTrueRange(0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetAverageTrueRange(9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.High = append(ohlc.High, 1337) + _, err = ohlc.GetAverageTrueRange(9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Low = append(ohlc.Low, 1337) + _, err = ohlc.GetAverageTrueRange(9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Close = append(ohlc.Close, 1337) + _, err = ohlc.GetAverageTrueRange(9) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetAverageTrueRange(1) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + wrap := Item{Candles: []Candle{{High: 1337, Low: 1337, Close: 1337}}} + _, err = wrap.GetAverageTrueRange(1) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestGetBollingerBands(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetBollingerBands(0, 0, 0, 5) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetBollingerBands(0, 0, 0, 5) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetBollingerBands(9, 0, 0, 5) + if !errors.Is(err, errInvalidDeviationMultiplier) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidDeviationMultiplier) + } + + _, err = ohlc.GetBollingerBands(9, 1, 0, 5) + if !errors.Is(err, errInvalidDeviationMultiplier) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidDeviationMultiplier) + } + + _, err = ohlc.GetBollingerBands(9, 1, 1, 5) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Close = append(ohlc.Close, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337) + _, err = ohlc.GetBollingerBands(10, 1, 1, 5) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetBollingerBands(9, 1, 1, 5) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + wrap := Item{Candles: []Candle{{Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}}} + _, err = wrap.GetBollingerBands(9, 1, 1, 5) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestGetCorrelationCoefficient(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetCorrelationCoefficient(nil, 0) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetCorrelationCoefficient(nil, 0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetCorrelationCoefficient(nil, 1) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetCorrelationCoefficient(nil, 2) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + _, err = ohlc.GetCorrelationCoefficient(&OHLC{}, 9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Close = append(ohlc.Close, 1337, 1337) + + _, err = ohlc.GetCorrelationCoefficient(&OHLC{}, 9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + _, err = ohlc.GetCorrelationCoefficient(&OHLC{Close: []float64{1337}}, 2) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + ohlc.Close = append(ohlc.Close, 1337) + _, err = ohlc.GetCorrelationCoefficient(&OHLC{Close: []float64{1337, 1337}}, 2) + if !errors.Is(err, errInvalidDataSetLengths) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidDataSetLengths) + } + + _, err = ohlc.GetCorrelationCoefficient(&OHLC{Close: []float64{1337, 1337, 1337}}, 2) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + wrap := Item{Candles: []Candle{{Close: 1337}, {Close: 1337}, {Close: 1337}}} + _, err = wrap.GetCorrelationCoefficient(&Item{Candles: []Candle{{Close: 1337}, {Close: 1337}, {Close: 1337}}}, 2) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestGetSimpleMovingAverage(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetSimpleMovingAverage(nil, 0) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetSimpleMovingAverage(nil, 0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetSimpleMovingAverage(nil, 9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + _, err = ohlc.GetSimpleMovingAverage([]float64{1337}, 9) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetSimpleMovingAverage([]float64{1337, 1337}, 2) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + wrap := Item{Candles: []Candle{{Close: 1337}, {Close: 1337}}} + _, err = wrap.GetSimpleMovingAverageOnClose(2) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestGetExponentialMovingAverage(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetExponentialMovingAverage(nil, 0) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetExponentialMovingAverage(nil, 0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetExponentialMovingAverage(nil, 9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + _, err = ohlc.GetExponentialMovingAverage([]float64{1337}, 9) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetExponentialMovingAverage([]float64{1337, 1337, 1337}, 2) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + wrap := Item{Candles: []Candle{{Close: 1337}, {Close: 1337}, {Close: 1337}}} + _, err = wrap.GetExponentialMovingAverageOnClose(2) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestGetMovingAverageConvergenceDivergence(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetMovingAverageConvergenceDivergence(nil, 0, 0, 0) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetMovingAverageConvergenceDivergence(nil, 0, 0, 0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetMovingAverageConvergenceDivergence(nil, 1, 0, 0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetMovingAverageConvergenceDivergence(nil, 1, 1, 0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetMovingAverageConvergenceDivergence(nil, 1, 2, 0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetMovingAverageConvergenceDivergence(nil, 1, 2, 1) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + _, err = ohlc.GetMovingAverageConvergenceDivergence([]float64{1337}, 1, 2, 2) + if !errors.Is(err, errNotEnoughData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNotEnoughData) + } + + _, err = ohlc.GetMovingAverageConvergenceDivergence([]float64{1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337}, 1, 2, 1) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + wrap := Item{Candles: []Candle{{Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}, {Close: 1337}}} + _, err = wrap.GetMovingAverageConvergenceDivergenceOnClose(1, 2, 1) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestGetMoneyFlowIndex(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetMoneyFlowIndex(0) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetMoneyFlowIndex(0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetMoneyFlowIndex(9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.High = append(ohlc.High, 1337, 1337, 1337, 1337, 1337, 1337) + _, err = ohlc.GetMoneyFlowIndex(9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Low = append(ohlc.Low, 1337, 1337, 1337, 1337, 1337, 1337) + _, err = ohlc.GetMoneyFlowIndex(9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Close = append(ohlc.Close, 1337, 1337, 1337, 1337, 1337, 1337) + _, err = ohlc.GetMoneyFlowIndex(9) + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Volume = append(ohlc.Volume, 1337, 1337, 1337, 1337, 1337) + _, err = ohlc.GetMoneyFlowIndex(5) + if !errors.Is(err, errInvalidDataSetLengths) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidDataSetLengths) + } + + ohlc.Volume = append(ohlc.Volume, 1337) + _, err = ohlc.GetMoneyFlowIndex(6) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetMoneyFlowIndex(3) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + wrap := Item{Candles: []Candle{ + {Close: 1337, High: 1337, Low: 1337, Volume: 1337}, + {Close: 1337, High: 1337, Low: 1337, Volume: 1337}, + {Close: 1337, High: 1337, Low: 1337, Volume: 1337}, + }} + _, err = wrap.GetMoneyFlowIndex(2) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestGetOnBalanceVolume(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetOnBalanceVolume() + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetOnBalanceVolume() + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Close = append(ohlc.Close, 1337, 1337, 1337, 1337, 1337, 1337) + _, err = ohlc.GetOnBalanceVolume() + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Volume = append(ohlc.Volume, 0.00000001) + _, err = ohlc.GetOnBalanceVolume() + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + wrap := Item{Candles: []Candle{{Close: 1337, Volume: 1337}}} + _, err = wrap.GetOnBalanceVolume() + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} + +func TestGetRelativeStrengthIndex(t *testing.T) { + t.Parallel() + + var ohlc *OHLC + _, err := ohlc.GetRelativeStrengthIndex(nil, 0) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetRelativeStrengthIndex(nil, 0) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + _, err = ohlc.GetRelativeStrengthIndex(nil, 9) + if !errors.Is(err, errNotEnoughData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNotEnoughData) + } + + _, err = ohlc.GetRelativeStrengthIndex([]float64{1337, 1337, 1337}, 9) + if !errors.Is(err, errInvalidPeriod) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidPeriod) + } + + wrap := Item{Candles: []Candle{{Close: 1337}, {Close: 1337}, {Close: 1337}}} + _, err = wrap.GetRelativeStrengthIndexOnClose(2) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } +} diff --git a/exchanges/kline/weighted_price.go b/exchanges/kline/weighted_price.go new file mode 100644 index 00000000..01ac2c62 --- /dev/null +++ b/exchanges/kline/weighted_price.go @@ -0,0 +1,137 @@ +package kline + +import ( + "errors" + "fmt" +) + +var ( + errInvalidElement = errors.New("invalid element") + errElementExceedsDataLength = errors.New("element exceeds data length") + errDataLengthMismatch = errors.New("data length mismatch") +) + +// GetAveragePrice returns the average price from the open, high, low and close +func (c *Candle) GetAveragePrice() float64 { + return (c.Open + c.High + c.Low + c.Close) / 4 +} + +// GetAveragePrice returns the average price from the open, high, low and close +func (o *OHLC) GetAveragePrice(element int) (float64, error) { + if o == nil { + return 0, fmt.Errorf("get average price %w", errNilOHLC) + } + if element < 0 { + return 0, fmt.Errorf("get average price %w", errInvalidElement) + } + check := element + 1 + if check > len(o.Open) || check > len(o.High) || check > len(o.Low) || check > len(o.Close) { + return 0, fmt.Errorf("get average price %w", errElementExceedsDataLength) + } + return (o.Open[element] + o.High[element] + o.Low[element] + o.Close[element]) / 4, nil +} + +// GetTWAP returns the time weighted average price for the specified period. +// NOTE: This assumes the most recent price is at the tail end of the slice. +// Based off: https://blog.quantinsti.com/twap/ +// Only returns one item as all other items are just the average price. +func (k *Item) GetTWAP() (float64, error) { + if len(k.Candles) == 0 { + return 0, fmt.Errorf("get twap %w", errNoData) + } + var cumAveragePrice float64 + for x := range k.Candles { + cumAveragePrice += k.Candles[x].GetAveragePrice() + } + return cumAveragePrice / float64(len(k.Candles)), nil +} + +// GetTWAP returns the time weighted average price for the specified period. +func (o *OHLC) GetTWAP() (float64, error) { + if o == nil { + return 0, fmt.Errorf("get twap %w", errNilOHLC) + } + if len(o.Open) == 0 || len(o.High) == 0 || len(o.Low) == 0 || len(o.Close) == 0 { + return 0, fmt.Errorf("get twap %w", errNoData) + } + if len(o.Open) != len(o.High) || len(o.Open) != len(o.Low) || len(o.Open) != len(o.Close) { + return 0, fmt.Errorf("get twap %w", errDataLengthMismatch) + } + + var cumAveragePrice float64 + for x := range o.Close { + avgPrice, err := o.GetAveragePrice(x) + if err != nil { + return 0, fmt.Errorf("get twap %w", err) + } + cumAveragePrice += avgPrice + } + return cumAveragePrice / float64(len(o.Close)), nil +} + +// GetTypicalPrice returns the typical average price from the high, low and +// close values. +func (c *Candle) GetTypicalPrice() float64 { + return (c.High + c.Low + c.Close) / 3 +} + +// GetTypicalPrice returns the typical average price from the high, low and +// close values. +func (o *OHLC) GetTypicalPrice(element int) (float64, error) { + if o == nil { + return 0, fmt.Errorf("get typical price %w", errNilOHLC) + } + if element < 0 { + return 0, fmt.Errorf("get typical price %w", errInvalidElement) + } + check := element + 1 + if check > len(o.High) || check > len(o.Low) || check > len(o.Close) { + return 0, fmt.Errorf("get typical price %w", errElementExceedsDataLength) + } + return (o.High[element] + o.Low[element] + o.Close[element]) / 3, nil +} + +// GetVWAPs returns the Volume Weighted Averages prices which are the cumulative +// average price with respect to the volume. +// NOTE: This assumes candles are sorted by time +// Based off: https://blog.quantinsti.com/vwap-strategy/ +func (k *Item) GetVWAPs() ([]float64, error) { + if len(k.Candles) == 0 { + return nil, fmt.Errorf("get vwap %w", errNoData) + } + store := make([]float64, len(k.Candles)) + var cumTotal, cumVolume float64 + for x := range k.Candles { + cumTotal += k.Candles[x].GetTypicalPrice() * k.Candles[x].Volume + cumVolume += k.Candles[x].Volume + store[x] = cumTotal / cumVolume + } + return store, nil +} + +// GetVWAPs returns the Volume Weighted Averages prices which are the cumulative +// average price with respect to the volume. +func (o *OHLC) GetVWAPs() ([]float64, error) { + if o == nil { + return nil, fmt.Errorf("get vwap %w", errNilOHLC) + } + if len(o.Open) == 0 || len(o.High) == 0 || len(o.Low) == 0 || len(o.Close) == 0 { + return nil, fmt.Errorf("get vwap %w", errNoData) + } + if len(o.Open) != len(o.High) || len(o.Open) != len(o.Low) || len(o.Open) != len(o.Close) { + return nil, fmt.Errorf("get vwap %w", errDataLengthMismatch) + } + + store := make([]float64, len(o.High)) + var cumTotal, cumVolume float64 + for x := range o.High { + typPrice, err := o.GetTypicalPrice(x) + if err != nil { + return nil, fmt.Errorf("get vwap %w", err) + } + cumTotal += typPrice * o.Volume[x] + cumVolume += o.Volume[x] + store[x] = cumTotal / cumVolume + } + return store, nil +} diff --git a/exchanges/kline/weighted_price_test.go b/exchanges/kline/weighted_price_test.go new file mode 100644 index 00000000..5ebb54dc --- /dev/null +++ b/exchanges/kline/weighted_price_test.go @@ -0,0 +1,313 @@ +package kline + +import ( + "errors" + "testing" + "time" +) + +func TestGetAveragePrice(t *testing.T) { + t.Parallel() + c := Candle{} + if c.GetAveragePrice() != 0 { + t.Fatal("unexpected value") + } + + c.High = 20 + if c.GetAveragePrice() != 5 { + t.Fatal("unexpected value") + } +} + +func TestGetAveragePrice_OHLC(t *testing.T) { + t.Parallel() + var ohlc *OHLC + _, err := ohlc.GetAveragePrice(-1) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetAveragePrice(-1) + if !errors.Is(err, errInvalidElement) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidElement) + } + + _, err = ohlc.GetAveragePrice(0) + if !errors.Is(err, errElementExceedsDataLength) { + t.Fatalf("received: '%v' but expected: '%v'", err, errElementExceedsDataLength) + } + + ohlc.High = append(ohlc.High, 20) + ohlc.Open = append(ohlc.Open, 0) + ohlc.Low = append(ohlc.Low, 0) + ohlc.Close = append(ohlc.Close, 0) + avgPrice, err := ohlc.GetAveragePrice(0) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if avgPrice != 5 { + t.Fatal("unexpected value") + } +} + +var twapdataset = []Candle{ + {Time: time.Date(2020, 6, 17, 0, 0, 0, 0, time.UTC), Close: 351.59, Open: 355.15, High: 355.40, Low: 351.09}, + {Time: time.Date(2020, 6, 16, 0, 0, 0, 0, time.UTC), Close: 352.08, Open: 351.46, High: 353.20, Low: 344.72}, + {Time: time.Date(2020, 6, 15, 0, 0, 0, 0, time.UTC), Close: 342.99, Open: 333.25, High: 345.68, Low: 332.58}, + {Time: time.Date(2020, 6, 12, 0, 0, 0, 0, time.UTC), Close: 338.80, Open: 344.72, High: 347.80, Low: 334.22}, + {Time: time.Date(2020, 6, 11, 0, 0, 0, 0, time.UTC), Close: 335.90, Open: 349.31, High: 351.06, Low: 335.48}, + {Time: time.Date(2020, 6, 10, 0, 0, 0, 0, time.UTC), Close: 352.84, Open: 347.90, High: 354.77, Low: 346.09}, + {Time: time.Date(2020, 6, 9, 0, 0, 0, 0, time.UTC), Close: 343.99, Open: 332.14, High: 345.61, Low: 332.01}, + {Time: time.Date(2020, 6, 8, 0, 0, 0, 0, time.UTC), Close: 333.46, Open: 330.25, High: 333.60, Low: 327.32}, + {Time: time.Date(2020, 6, 5, 0, 0, 0, 0, time.UTC), Close: 331.50, Open: 323.35, High: 331.75, Low: 323.23}, + {Time: time.Date(2020, 6, 4, 0, 0, 0, 0, time.UTC), Close: 322.32, Open: 324.39, High: 325.62, Low: 320.78}, + {Time: time.Date(2020, 6, 3, 0, 0, 0, 0, time.UTC), Close: 325.12, Open: 324.66, High: 326.20, Low: 322.30}, + {Time: time.Date(2020, 6, 2, 0, 0, 0, 0, time.UTC), Close: 323.34, Open: 320.75, High: 323.44, Low: 318.93}, + {Time: time.Date(2020, 6, 1, 0, 0, 0, 0, time.UTC), Close: 321.85, Open: 317.75, High: 322.35, Low: 317.21}, + {Time: time.Date(2020, 5, 29, 0, 0, 0, 0, time.UTC), Close: 317.94, Open: 319.25, High: 321.15, Low: 316.47}, + {Time: time.Date(2020, 5, 28, 0, 0, 0, 0, time.UTC), Close: 318.25, Open: 316.77, High: 323.44, Low: 315.63}, + {Time: time.Date(2020, 5, 27, 0, 0, 0, 0, time.UTC), Close: 318.11, Open: 316.14, High: 318.71, Low: 313.09}, + {Time: time.Date(2020, 5, 26, 0, 0, 0, 0, time.UTC), Close: 316.73, Open: 323.50, High: 324.24, Low: 316.50}, + {Time: time.Date(2020, 5, 22, 0, 0, 0, 0, time.UTC), Close: 318.89, Open: 315.77, High: 319.23, Low: 315.35}, + {Time: time.Date(2020, 5, 21, 0, 0, 0, 0, time.UTC), Close: 316.85, Open: 318.66, High: 320.89, Low: 315.87}, + {Time: time.Date(2020, 5, 20, 0, 0, 0, 0, time.UTC), Close: 319.23, Open: 316.68, High: 319.52, Low: 316.20}, + {Time: time.Date(2020, 5, 19, 0, 0, 0, 0, time.UTC), Close: 313.14, Open: 315.03, High: 318.52, Low: 313.01}, + {Time: time.Date(2020, 5, 18, 0, 0, 0, 0, time.UTC), Close: 314.96, Open: 313.17, High: 316.50, Low: 310.32}, +} + +func TestGetTWAP_OHLC(t *testing.T) { + t.Parallel() + var ohlc *OHLC + _, err := ohlc.GetTWAP() + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetTWAP() + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Open = append(ohlc.Open, 20) + ohlc.High = append(ohlc.High, 20) + ohlc.Low = append(ohlc.Low, 20) + ohlc.Close = append(ohlc.Close, 20, 20) + _, err = ohlc.GetTWAP() + if !errors.Is(err, errDataLengthMismatch) { + t.Fatalf("received: '%v' but expected: '%v'", err, errDataLengthMismatch) + } + + i := Item{} + i.Candles = twapdataset + + ohlc = i.GetOHLC() + twap, err := ohlc.GetTWAP() + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if twap != 328.147840909091 { + t.Fatal("unexpected value returned from data-set") + } +} + +func TestGetTWAP(t *testing.T) { + t.Parallel() + candles := Item{} + if _, err := candles.GetTWAP(); !errors.Is(err, errNoData) { + t.Fatal(err) + } + + candles.Candles = twapdataset + twap, err := candles.GetTWAP() + if err != nil { + t.Fatal(err) + } + + if twap != 328.147840909091 { + t.Fatal("unexpected value returned from data-set") + } +} + +var vwapdataset = []Candle{ + {Time: time.Date(2019, 10, 10, 9, 31, 0, 0, time.UTC), Open: 245.2903, High: 245.516, Low: 244.7652, Close: 244.8702, Volume: 103033}, + {Time: time.Date(2019, 10, 10, 9, 32, 0, 0, time.UTC), Open: 245.0807, High: 245.0807, Low: 244.55, Close: 244.66, Volume: 21168}, + {Time: time.Date(2019, 10, 10, 9, 33, 0, 0, time.UTC), Open: 244.58, High: 245.8, Low: 244.55, Close: 245.6, Volume: 36544}, + {Time: time.Date(2019, 10, 10, 9, 34, 0, 0, time.UTC), Open: 245.7097, High: 246.09, Low: 245.57, Close: 245.92, Volume: 30057}, + {Time: time.Date(2019, 10, 10, 9, 35, 0, 0, time.UTC), Open: 245.62, High: 245.62, Low: 245.62, Close: 245.62, Volume: 26301}, + {Time: time.Date(2019, 10, 10, 9, 36, 0, 0, time.UTC), Open: 245.7126, High: 246.44, Low: 245.7126, Close: 246.188, Volume: 31494}, + {Time: time.Date(2019, 10, 10, 9, 37, 0, 0, time.UTC), Open: 246.46, High: 246.46, Low: 246.45, Close: 246.45, Volume: 24271}, + {Time: time.Date(2019, 10, 10, 9, 38, 0, 0, time.UTC), Open: 246.755, High: 246.755, Low: 246.25, Close: 246.25, Volume: 37951}, + {Time: time.Date(2019, 10, 10, 9, 39, 0, 0, time.UTC), Open: 246.2818, High: 246.655, Low: 246.2818, Close: 246.655, Volume: 15324}, + {Time: time.Date(2019, 10, 10, 9, 40, 0, 0, time.UTC), Open: 246.78, High: 246.78, Low: 246.56, Close: 246.762, Volume: 23285}, + {Time: time.Date(2019, 10, 10, 9, 41, 0, 0, time.UTC), Open: 246.75, High: 246.75, Low: 246.38, Close: 246.5, Volume: 23365}, + {Time: time.Date(2019, 10, 10, 9, 42, 0, 0, time.UTC), Open: 246.17, High: 246.17, Low: 246.17, Close: 246.17, Volume: 16130}, + {Time: time.Date(2019, 10, 10, 9, 43, 0, 0, time.UTC), Open: 246.135, High: 246.135, Low: 245.82, Close: 245.82, Volume: 27227}, + {Time: time.Date(2019, 10, 10, 9, 44, 0, 0, time.UTC), Open: 245.9335, High: 245.9335, Low: 245.91, Close: 245.91, Volume: 14464}, + {Time: time.Date(2019, 10, 10, 9, 45, 0, 0, time.UTC), Open: 246.41, High: 246.41, Low: 246.41, Close: 246.41, Volume: 17156}, + {Time: time.Date(2019, 10, 10, 9, 46, 0, 0, time.UTC), Open: 246.44, High: 246.46, Low: 246.1683, Close: 246.1683, Volume: 23938}, + {Time: time.Date(2019, 10, 10, 9, 47, 0, 0, time.UTC), Open: 246.2857, High: 246.57, Low: 246.2857, Close: 246.57, Volume: 70833}, + {Time: time.Date(2019, 10, 10, 9, 48, 0, 0, time.UTC), Open: 246.6, High: 247.47, Low: 246.6, Close: 247.47, Volume: 59743}, + {Time: time.Date(2019, 10, 10, 9, 49, 0, 0, time.UTC), Open: 247.49, High: 247.65, Low: 247.49, Close: 247.65, Volume: 71995}, + {Time: time.Date(2019, 10, 10, 9, 50, 0, 0, time.UTC), Open: 247.685, High: 247.801, Low: 247.65, Close: 247.69, Volume: 46038}, + {Time: time.Date(2019, 10, 10, 9, 51, 0, 0, time.UTC), Open: 247.95, High: 248.74, Low: 247.95, Close: 248.74, Volume: 103773}, + {Time: time.Date(2019, 10, 10, 9, 52, 0, 0, time.UTC), Open: 248.56, High: 248.56, Low: 247.95, Close: 247.95, Volume: 73810}, + {Time: time.Date(2019, 10, 10, 9, 53, 0, 0, time.UTC), Open: 247.93, High: 247.93, Low: 247.6614, Close: 247.6614, Volume: 29784}, + {Time: time.Date(2019, 10, 10, 9, 54, 0, 0, time.UTC), Open: 247.74, High: 247.76, Low: 247.65, Close: 247.76, Volume: 37138}, + {Time: time.Date(2019, 10, 10, 9, 55, 0, 0, time.UTC), Open: 247.93, High: 248.03, Low: 247.93, Close: 248.03, Volume: 53166}, + {Time: time.Date(2019, 10, 10, 9, 56, 0, 0, time.UTC), Open: 247.91, High: 248.44, Low: 247.91, Close: 248.44, Volume: 40789}, + {Time: time.Date(2019, 10, 10, 9, 57, 0, 0, time.UTC), Open: 248.52, High: 248.52, Low: 248.3154, Close: 248.3154, Volume: 51988}, + {Time: time.Date(2019, 10, 10, 9, 58, 0, 0, time.UTC), Open: 248.4409, High: 248.62, Low: 248.4409, Close: 248.62, Volume: 53405}, + {Time: time.Date(2019, 10, 10, 9, 59, 0, 0, time.UTC), Open: 248.9199, High: 248.9199, Low: 248.9199, Close: 248.9199, Volume: 85348}, + {Time: time.Date(2019, 10, 10, 10, 0, 0, 0, time.UTC), Open: 248.91, High: 249.08, Low: 248.42, Close: 248.72, Volume: 58270}, +} + +func TestGetVWAPs(t *testing.T) { + t.Parallel() + candles := Item{} + if _, err := candles.GetVWAPs(); !errors.Is(err, errNoData) { + t.Fatal(err) + } + + candles.Candles = vwapdataset + vwap, err := candles.GetVWAPs() + if err != nil { + t.Fatal(err) + } + + assert(t, vwap[0], 245.05046666666664) + assert(t, vwap[1], 245.00156932123465) + assert(t, vwap[2], 245.07320400593073) + assert(t, vwap[3], 245.19714781780763) + assert(t, vwap[4], 245.248374356565) + assert(t, vwap[5], 245.35797872352975) + assert(t, vwap[6], 245.45540807301208) + assert(t, vwap[7], 245.57298124760712) + assert(t, vwap[8], 245.61797546720302) + assert(t, vwap[9], 245.6901232761351) + assert(t, vwap[10], 245.7435986712912) + assert(t, vwap[11], 245.76128302894574) + assert(t, vwap[12], 245.771994363731) + assert(t, vwap[13], 245.7768929849006) + assert(t, vwap[14], 245.80115004533573) + assert(t, vwap[15], 245.82471633454026) + assert(t, vwap[16], 245.90964645148168) + assert(t, vwap[17], 246.0356579876492) + assert(t, vwap[18], 246.20233204964117) + assert(t, vwap[19], 246.29892677543359) + assert(t, vwap[20], 246.57315726207088) + assert(t, vwap[21], 246.70305234595537) + assert(t, vwap[22], 246.73669536160304) + assert(t, vwap[23], 246.7746731036053) + assert(t, vwap[24], 246.83849361010806) + assert(t, vwap[25], 246.89338504378165) + assert(t, vwap[26], 246.96313273581723) + assert(t, vwap[27], 247.03640100225914) + assert(t, vwap[28], 247.16505290840146) + assert(t, vwap[29], 247.23522648930867) +} + +func TestGetVWAPs_OHLC(t *testing.T) { + t.Parallel() + var ohlc *OHLC + _, err := ohlc.GetVWAPs() + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetVWAPs() + if !errors.Is(err, errNoData) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNoData) + } + + ohlc.Open = append(ohlc.Open, 20) + ohlc.High = append(ohlc.High, 20) + ohlc.Low = append(ohlc.Low, 20) + ohlc.Close = append(ohlc.Close, 20, 20) + + _, err = ohlc.GetVWAPs() + if !errors.Is(err, errDataLengthMismatch) { + t.Fatalf("received: '%v' but expected: '%v'", err, errDataLengthMismatch) + } + + ohlc = (&Item{Candles: vwapdataset}).GetOHLC() + + vwap, err := ohlc.GetVWAPs() + if err != nil { + t.Fatal(err) + } + + assert(t, vwap[0], 245.05046666666664) + assert(t, vwap[1], 245.00156932123465) + assert(t, vwap[2], 245.07320400593073) + assert(t, vwap[3], 245.19714781780763) + assert(t, vwap[4], 245.248374356565) + assert(t, vwap[5], 245.35797872352975) + assert(t, vwap[6], 245.45540807301208) + assert(t, vwap[7], 245.57298124760712) + assert(t, vwap[8], 245.61797546720302) + assert(t, vwap[9], 245.6901232761351) + assert(t, vwap[10], 245.7435986712912) + assert(t, vwap[11], 245.76128302894574) + assert(t, vwap[12], 245.771994363731) + assert(t, vwap[13], 245.7768929849006) + assert(t, vwap[14], 245.80115004533573) + assert(t, vwap[15], 245.82471633454026) + assert(t, vwap[16], 245.90964645148168) + assert(t, vwap[17], 246.0356579876492) + assert(t, vwap[18], 246.20233204964117) + assert(t, vwap[19], 246.29892677543359) + assert(t, vwap[20], 246.57315726207088) + assert(t, vwap[21], 246.70305234595537) + assert(t, vwap[22], 246.73669536160304) + assert(t, vwap[23], 246.7746731036053) + assert(t, vwap[24], 246.83849361010806) + assert(t, vwap[25], 246.89338504378165) + assert(t, vwap[26], 246.96313273581723) + assert(t, vwap[27], 247.03640100225914) + assert(t, vwap[28], 247.16505290840146) + assert(t, vwap[29], 247.23522648930867) +} + +func TestGetTypicalPrice_OHLC(t *testing.T) { + t.Parallel() + var ohlc *OHLC + _, err := ohlc.GetTypicalPrice(-1) + if !errors.Is(err, errNilOHLC) { + t.Fatalf("received: '%v' but expected: '%v'", err, errNilOHLC) + } + + ohlc = &OHLC{} + _, err = ohlc.GetTypicalPrice(-1) + if !errors.Is(err, errInvalidElement) { + t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidElement) + } + + _, err = ohlc.GetTypicalPrice(0) + if !errors.Is(err, errElementExceedsDataLength) { + t.Fatalf("received: '%v' but expected: '%v'", err, errElementExceedsDataLength) + } + + ohlc.High = append(ohlc.High, 15) + ohlc.Low = append(ohlc.Low, 0) + ohlc.Close = append(ohlc.Close, 0) + avgPrice, err := ohlc.GetTypicalPrice(0) + if !errors.Is(err, nil) { + t.Fatalf("received: '%v' but expected: '%v'", err, nil) + } + + if avgPrice != 5 { + t.Fatal("unexpected value") + } +} + +func assert(t *testing.T, received, expected float64) { + t.Helper() + if received != expected { + t.Fatalf("received: '%v' but expected: '%v'", received, expected) + } +} diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index c97daee2..7ecc90e2 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -989,7 +989,7 @@ func TestValidateCandlesRequest(t *testing.T) { t.Error(err) } _, err = z.validateCandlesRequest(currency.EMPTYPAIR, asset.Spot, time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC), time.Date(2020, 1, 1, 1, 1, 1, 3, time.UTC), kline.OneHour) - if err != nil && err.Error() != "pair not enabled" { + if !errors.Is(err, kline.ErrValidatingParams) { t.Error(err) } var p currency.Pair diff --git a/gctrpc/rpc.pb.go b/gctrpc/rpc.pb.go index a4491808..815f895f 100644 --- a/gctrpc/rpc.pb.go +++ b/gctrpc/rpc.pb.go @@ -11768,6 +11768,267 @@ func (*ShutdownResponse) Descriptor() ([]byte, []int) { return file_rpc_proto_rawDescGZIP(), []int{179} } +type GetTechnicalAnalysisRequest 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"` + AlgorithmType string `protobuf:"bytes,4,opt,name=algorithm_type,json=algorithmType,proto3" json:"algorithm_type,omitempty"` + Interval int64 `protobuf:"varint,5,opt,name=interval,proto3" json:"interval,omitempty"` + Start *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=start,proto3" json:"start,omitempty"` + End *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=end,proto3" json:"end,omitempty"` + Period int64 `protobuf:"varint,8,opt,name=period,proto3" json:"period,omitempty"` + FastPeriod int64 `protobuf:"varint,9,opt,name=fast_period,json=fastPeriod,proto3" json:"fast_period,omitempty"` + SlowPeriod int64 `protobuf:"varint,10,opt,name=slow_period,json=slowPeriod,proto3" json:"slow_period,omitempty"` + StandardDeviationUp float64 `protobuf:"fixed64,11,opt,name=standard_deviation_up,json=standardDeviationUp,proto3" json:"standard_deviation_up,omitempty"` + StandardDeviationDown float64 `protobuf:"fixed64,12,opt,name=standard_deviation_down,json=standardDeviationDown,proto3" json:"standard_deviation_down,omitempty"` + MovingAverageType int64 `protobuf:"varint,13,opt,name=moving_average_type,json=movingAverageType,proto3" json:"moving_average_type,omitempty"` + OtherExchange string `protobuf:"bytes,14,opt,name=other_exchange,json=otherExchange,proto3" json:"other_exchange,omitempty"` + OtherPair *CurrencyPair `protobuf:"bytes,15,opt,name=other_pair,json=otherPair,proto3" json:"other_pair,omitempty"` + OtherAssetType string `protobuf:"bytes,16,opt,name=other_asset_type,json=otherAssetType,proto3" json:"other_asset_type,omitempty"` +} + +func (x *GetTechnicalAnalysisRequest) Reset() { + *x = GetTechnicalAnalysisRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[180] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTechnicalAnalysisRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTechnicalAnalysisRequest) ProtoMessage() {} + +func (x *GetTechnicalAnalysisRequest) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[180] + 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 GetTechnicalAnalysisRequest.ProtoReflect.Descriptor instead. +func (*GetTechnicalAnalysisRequest) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{180} +} + +func (x *GetTechnicalAnalysisRequest) GetExchange() string { + if x != nil { + return x.Exchange + } + return "" +} + +func (x *GetTechnicalAnalysisRequest) GetPair() *CurrencyPair { + if x != nil { + return x.Pair + } + return nil +} + +func (x *GetTechnicalAnalysisRequest) GetAssetType() string { + if x != nil { + return x.AssetType + } + return "" +} + +func (x *GetTechnicalAnalysisRequest) GetAlgorithmType() string { + if x != nil { + return x.AlgorithmType + } + return "" +} + +func (x *GetTechnicalAnalysisRequest) GetInterval() int64 { + if x != nil { + return x.Interval + } + return 0 +} + +func (x *GetTechnicalAnalysisRequest) GetStart() *timestamppb.Timestamp { + if x != nil { + return x.Start + } + return nil +} + +func (x *GetTechnicalAnalysisRequest) GetEnd() *timestamppb.Timestamp { + if x != nil { + return x.End + } + return nil +} + +func (x *GetTechnicalAnalysisRequest) GetPeriod() int64 { + if x != nil { + return x.Period + } + return 0 +} + +func (x *GetTechnicalAnalysisRequest) GetFastPeriod() int64 { + if x != nil { + return x.FastPeriod + } + return 0 +} + +func (x *GetTechnicalAnalysisRequest) GetSlowPeriod() int64 { + if x != nil { + return x.SlowPeriod + } + return 0 +} + +func (x *GetTechnicalAnalysisRequest) GetStandardDeviationUp() float64 { + if x != nil { + return x.StandardDeviationUp + } + return 0 +} + +func (x *GetTechnicalAnalysisRequest) GetStandardDeviationDown() float64 { + if x != nil { + return x.StandardDeviationDown + } + return 0 +} + +func (x *GetTechnicalAnalysisRequest) GetMovingAverageType() int64 { + if x != nil { + return x.MovingAverageType + } + return 0 +} + +func (x *GetTechnicalAnalysisRequest) GetOtherExchange() string { + if x != nil { + return x.OtherExchange + } + return "" +} + +func (x *GetTechnicalAnalysisRequest) GetOtherPair() *CurrencyPair { + if x != nil { + return x.OtherPair + } + return nil +} + +func (x *GetTechnicalAnalysisRequest) GetOtherAssetType() string { + if x != nil { + return x.OtherAssetType + } + return "" +} + +type ListOfSignals struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Signals []float64 `protobuf:"fixed64,1,rep,packed,name=signals,proto3" json:"signals,omitempty"` +} + +func (x *ListOfSignals) Reset() { + *x = ListOfSignals{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[181] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListOfSignals) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListOfSignals) ProtoMessage() {} + +func (x *ListOfSignals) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[181] + 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 ListOfSignals.ProtoReflect.Descriptor instead. +func (*ListOfSignals) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{181} +} + +func (x *ListOfSignals) GetSignals() []float64 { + if x != nil { + return x.Signals + } + return nil +} + +type GetTechnicalAnalysisResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Signals map[string]*ListOfSignals `protobuf:"bytes,1,rep,name=signals,proto3" json:"signals,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *GetTechnicalAnalysisResponse) Reset() { + *x = GetTechnicalAnalysisResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_rpc_proto_msgTypes[182] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTechnicalAnalysisResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTechnicalAnalysisResponse) ProtoMessage() {} + +func (x *GetTechnicalAnalysisResponse) ProtoReflect() protoreflect.Message { + mi := &file_rpc_proto_msgTypes[182] + 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 GetTechnicalAnalysisResponse.ProtoReflect.Descriptor instead. +func (*GetTechnicalAnalysisResponse) Descriptor() ([]byte, []int) { + return file_rpc_proto_rawDescGZIP(), []int{182} +} + +func (x *GetTechnicalAnalysisResponse) GetSignals() map[string]*ListOfSignals { + if x != nil { + return x.Signals + } + return nil +} + var File_rpc_proto protoreflect.FileDescriptor var file_rpc_proto_rawDesc = []byte{ @@ -13396,7 +13657,64 @@ var file_rpc_proto_rawDesc = []byte{ 0x53, 0x70, 0x6f, 0x74, 0x4d, 0x61, 0x72, 0x67, 0x69, 0x6e, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x0a, 0x10, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x32, 0xec, 0x59, 0x0a, 0x15, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, + 0x65, 0x22, 0xa1, 0x05, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, + 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 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, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, + 0x74, 0x68, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x72, 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, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, + 0x6e, 0x64, 0x18, 0x07, 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, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x70, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x66, 0x61, 0x73, 0x74, 0x50, 0x65, 0x72, 0x69, + 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, + 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x6c, 0x6f, 0x77, 0x50, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, + 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x70, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x13, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x44, 0x65, 0x76, 0x69, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x74, 0x61, 0x6e, 0x64, + 0x61, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x6f, + 0x77, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x15, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, + 0x72, 0x64, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x6f, 0x77, 0x6e, 0x12, + 0x2e, 0x0a, 0x13, 0x6d, 0x6f, 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x76, 0x65, 0x72, 0x61, 0x67, + 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6d, 0x6f, + 0x76, 0x69, 0x6e, 0x67, 0x41, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x25, 0x0a, 0x0e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x45, 0x78, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x33, 0x0a, 0x0a, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, + 0x70, 0x61, 0x69, 0x72, 0x18, 0x0f, 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, 0x09, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x50, 0x61, 0x69, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6f, + 0x74, 0x68, 0x65, 0x72, 0x5f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x29, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x66, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x01, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, + 0x22, 0xbe, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x4b, 0x0a, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, + 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x1a, 0x51, + 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 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, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x66, 0x53, + 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x32, 0xf2, 0x5a, 0x0a, 0x15, 0x47, 0x6f, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x54, 0x72, 0x61, 0x64, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 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, @@ -14115,10 +14433,18 @@ var file_rpc_proto_rawDesc = []byte{ 0x1a, 0x18, 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, - 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, - 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, - 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x83, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, + 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x12, 0x23, 0x2e, 0x67, 0x63, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x41, + 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, + 0x2e, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x63, 0x68, 0x6e, + 0x69, 0x63, 0x61, 0x6c, 0x41, 0x6e, 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 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, 0x74, 0x65, 0x63, 0x68, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x61, 0x6e, + 0x61, 0x6c, 0x79, 0x73, 0x69, 0x73, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x68, 0x72, 0x61, 0x73, 0x68, 0x65, 0x72, 0x2d, 0x63, 0x6f, + 0x72, 0x70, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x74, 0x72, 0x61, 0x64, 0x65, + 0x72, 0x2f, 0x67, 0x63, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -14133,7 +14459,7 @@ func file_rpc_proto_rawDescGZIP() []byte { return file_rpc_proto_rawDescData } -var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 193) +var file_rpc_proto_msgTypes = make([]protoimpl.MessageInfo, 197) var file_rpc_proto_goTypes = []interface{}{ (*GetInfoRequest)(nil), // 0: gctrpc.GetInfoRequest (*GetInfoResponse)(nil), // 1: gctrpc.GetInfoResponse @@ -14315,29 +14641,33 @@ var file_rpc_proto_goTypes = []interface{}{ (*CollateralUsedBreakdown)(nil), // 177: gctrpc.CollateralUsedBreakdown (*ShutdownRequest)(nil), // 178: gctrpc.ShutdownRequest (*ShutdownResponse)(nil), // 179: gctrpc.ShutdownResponse - nil, // 180: gctrpc.GetInfoResponse.SubsystemStatusEntry - nil, // 181: gctrpc.GetInfoResponse.RpcEndpointsEntry - nil, // 182: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - nil, // 183: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - nil, // 184: gctrpc.GetRPCEndpointsResponse.EndpointsEntry - nil, // 185: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - nil, // 186: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry - nil, // 187: gctrpc.OnlineCoins.CoinsEntry - nil, // 188: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry - nil, // 189: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry - nil, // 190: gctrpc.Orders.OrderStatusEntry - nil, // 191: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry - nil, // 192: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry - (*timestamppb.Timestamp)(nil), // 193: google.protobuf.Timestamp + (*GetTechnicalAnalysisRequest)(nil), // 180: gctrpc.GetTechnicalAnalysisRequest + (*ListOfSignals)(nil), // 181: gctrpc.ListOfSignals + (*GetTechnicalAnalysisResponse)(nil), // 182: gctrpc.GetTechnicalAnalysisResponse + nil, // 183: gctrpc.GetInfoResponse.SubsystemStatusEntry + nil, // 184: gctrpc.GetInfoResponse.RpcEndpointsEntry + nil, // 185: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + nil, // 186: gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + nil, // 187: gctrpc.GetRPCEndpointsResponse.EndpointsEntry + nil, // 188: gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + nil, // 189: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + nil, // 190: gctrpc.OnlineCoins.CoinsEntry + nil, // 191: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + nil, // 192: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + nil, // 193: gctrpc.Orders.OrderStatusEntry + nil, // 194: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + nil, // 195: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + nil, // 196: gctrpc.GetTechnicalAnalysisResponse.SignalsEntry + (*timestamppb.Timestamp)(nil), // 197: google.protobuf.Timestamp } var file_rpc_proto_depIdxs = []int32{ - 180, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry - 181, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry - 182, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry - 183, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry - 184, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry - 185, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry - 186, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry + 183, // 0: gctrpc.GetInfoResponse.subsystem_status:type_name -> gctrpc.GetInfoResponse.SubsystemStatusEntry + 184, // 1: gctrpc.GetInfoResponse.rpc_endpoints:type_name -> gctrpc.GetInfoResponse.RpcEndpointsEntry + 185, // 2: gctrpc.GetCommunicationRelayersResponse.communication_relayers:type_name -> gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry + 186, // 3: gctrpc.GetSusbsytemsResponse.subsystems_status:type_name -> gctrpc.GetSusbsytemsResponse.SubsystemsStatusEntry + 187, // 4: gctrpc.GetRPCEndpointsResponse.endpoints:type_name -> gctrpc.GetRPCEndpointsResponse.EndpointsEntry + 188, // 5: gctrpc.GetExchangeOTPsResponse.otp_codes:type_name -> gctrpc.GetExchangeOTPsResponse.OtpCodesEntry + 189, // 6: gctrpc.GetExchangeInfoResponse.supported_assets:type_name -> gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry 21, // 7: gctrpc.GetTickerRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 8: gctrpc.TickerResponse.pair:type_name -> gctrpc.CurrencyPair 22, // 9: gctrpc.Tickers.tickers:type_name -> gctrpc.TickerResponse @@ -14352,12 +14682,12 @@ var file_rpc_proto_depIdxs = []int32{ 33, // 18: gctrpc.GetAccountInfoResponse.accounts:type_name -> gctrpc.Account 38, // 19: gctrpc.GetPortfolioResponse.portfolio:type_name -> gctrpc.PortfolioAddress 43, // 20: gctrpc.OfflineCoins.addresses:type_name -> gctrpc.OfflineCoinSummary - 187, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry + 190, // 21: gctrpc.OnlineCoins.coins:type_name -> gctrpc.OnlineCoins.CoinsEntry 42, // 22: gctrpc.GetPortfolioSummaryResponse.coin_totals:type_name -> gctrpc.Coin 42, // 23: gctrpc.GetPortfolioSummaryResponse.coins_offline:type_name -> gctrpc.Coin - 188, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry + 191, // 24: gctrpc.GetPortfolioSummaryResponse.coins_offline_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry 42, // 25: gctrpc.GetPortfolioSummaryResponse.coins_online:type_name -> gctrpc.Coin - 189, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry + 192, // 26: gctrpc.GetPortfolioSummaryResponse.coins_online_summary:type_name -> gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry 51, // 27: gctrpc.GetForexProvidersResponse.forex_providers:type_name -> gctrpc.ForexProvider 54, // 28: gctrpc.GetForexRatesResponse.forex_rates:type_name -> gctrpc.ForexRatesConversion 57, // 29: gctrpc.OrderDetails.trades:type_name -> gctrpc.TradeHistory @@ -14371,7 +14701,7 @@ var file_rpc_proto_depIdxs = []int32{ 21, // 37: gctrpc.WhaleBombRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 38: gctrpc.CancelOrderRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 39: gctrpc.CancelBatchOrdersRequest.pair:type_name -> gctrpc.CurrencyPair - 190, // 40: gctrpc.Orders.order_status:type_name -> gctrpc.Orders.OrderStatusEntry + 193, // 40: gctrpc.Orders.order_status:type_name -> gctrpc.Orders.OrderStatusEntry 69, // 41: gctrpc.CancelBatchOrdersResponse.orders:type_name -> gctrpc.Orders 69, // 42: gctrpc.CancelAllOrdersResponse.orders:type_name -> gctrpc.Orders 74, // 43: gctrpc.GetEventsResponse.condition_params:type_name -> gctrpc.ConditionParams @@ -14379,16 +14709,16 @@ var file_rpc_proto_depIdxs = []int32{ 74, // 45: gctrpc.AddEventRequest.condition_params:type_name -> gctrpc.ConditionParams 21, // 46: gctrpc.AddEventRequest.pair:type_name -> gctrpc.CurrencyPair 80, // 47: gctrpc.DepositAddresses.addresses:type_name -> gctrpc.DepositAddress - 191, // 48: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry + 194, // 48: gctrpc.GetCryptocurrencyDepositAddressesResponse.addresses:type_name -> gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry 95, // 49: gctrpc.WithdrawalEventByIDResponse.event:type_name -> gctrpc.WithdrawalEventResponse 95, // 50: gctrpc.WithdrawalEventsByExchangeResponse.event:type_name -> gctrpc.WithdrawalEventResponse 96, // 51: gctrpc.WithdrawalEventResponse.exchange:type_name -> gctrpc.WithdrawlExchangeEvent 97, // 52: gctrpc.WithdrawalEventResponse.request:type_name -> gctrpc.WithdrawalRequestEvent - 193, // 53: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp - 193, // 54: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp + 197, // 53: gctrpc.WithdrawalEventResponse.created_at:type_name -> google.protobuf.Timestamp + 197, // 54: gctrpc.WithdrawalEventResponse.updated_at:type_name -> google.protobuf.Timestamp 98, // 55: gctrpc.WithdrawalRequestEvent.fiat:type_name -> gctrpc.FiatWithdrawalEvent 99, // 56: gctrpc.WithdrawalRequestEvent.crypto:type_name -> gctrpc.CryptoWithdrawalEvent - 192, // 57: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry + 195, // 57: gctrpc.GetExchangePairsResponse.supported_assets:type_name -> gctrpc.GetExchangePairsResponse.SupportedAssetsEntry 21, // 58: gctrpc.SetExchangePairRequest.pairs:type_name -> gctrpc.CurrencyPair 21, // 59: gctrpc.GetOrderbookStreamRequest.pair:type_name -> gctrpc.CurrencyPair 21, // 60: gctrpc.GetTickerStreamRequest.pair:type_name -> gctrpc.CurrencyPair @@ -14425,214 +14755,222 @@ var file_rpc_proto_depIdxs = []int32{ 175, // 91: gctrpc.GetCollateralResponse.currency_breakdown:type_name -> gctrpc.CollateralForCurrency 176, // 92: gctrpc.GetCollateralResponse.position_breakdown:type_name -> gctrpc.CollateralByPosition 177, // 93: gctrpc.CollateralForCurrency.used_breakdown:type_name -> gctrpc.CollateralUsedBreakdown - 9, // 94: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 3, // 95: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer - 9, // 96: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint - 18, // 97: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 44, // 98: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary - 45, // 99: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins - 46, // 100: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins - 81, // 101: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses - 18, // 102: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported - 0, // 103: gctrpc.GoCryptoTraderService.GetInfo:input_type -> gctrpc.GetInfoRequest - 6, // 104: gctrpc.GoCryptoTraderService.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest - 5, // 105: gctrpc.GoCryptoTraderService.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 5, // 106: gctrpc.GoCryptoTraderService.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest - 8, // 107: gctrpc.GoCryptoTraderService.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest - 2, // 108: gctrpc.GoCryptoTraderService.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest - 12, // 109: gctrpc.GoCryptoTraderService.GetExchanges:input_type -> gctrpc.GetExchangesRequest - 11, // 110: gctrpc.GoCryptoTraderService.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 111: gctrpc.GoCryptoTraderService.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest - 11, // 112: gctrpc.GoCryptoTraderService.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest - 15, // 113: gctrpc.GoCryptoTraderService.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest - 11, // 114: gctrpc.GoCryptoTraderService.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest - 20, // 115: gctrpc.GoCryptoTraderService.GetTicker:input_type -> gctrpc.GetTickerRequest - 23, // 116: gctrpc.GoCryptoTraderService.GetTickers:input_type -> gctrpc.GetTickersRequest - 26, // 117: gctrpc.GoCryptoTraderService.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest - 29, // 118: gctrpc.GoCryptoTraderService.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest - 32, // 119: gctrpc.GoCryptoTraderService.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 120: gctrpc.GoCryptoTraderService.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest - 32, // 121: gctrpc.GoCryptoTraderService.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest - 36, // 122: gctrpc.GoCryptoTraderService.GetConfig:input_type -> gctrpc.GetConfigRequest - 39, // 123: gctrpc.GoCryptoTraderService.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest - 41, // 124: gctrpc.GoCryptoTraderService.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest - 48, // 125: gctrpc.GoCryptoTraderService.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest - 49, // 126: gctrpc.GoCryptoTraderService.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest - 50, // 127: gctrpc.GoCryptoTraderService.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest - 53, // 128: gctrpc.GoCryptoTraderService.GetForexRates:input_type -> gctrpc.GetForexRatesRequest - 58, // 129: gctrpc.GoCryptoTraderService.GetOrders:input_type -> gctrpc.GetOrdersRequest - 60, // 130: gctrpc.GoCryptoTraderService.GetOrder:input_type -> gctrpc.GetOrderRequest - 61, // 131: gctrpc.GoCryptoTraderService.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest - 64, // 132: gctrpc.GoCryptoTraderService.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest - 66, // 133: gctrpc.GoCryptoTraderService.WhaleBomb:input_type -> gctrpc.WhaleBombRequest - 67, // 134: gctrpc.GoCryptoTraderService.CancelOrder:input_type -> gctrpc.CancelOrderRequest - 68, // 135: gctrpc.GoCryptoTraderService.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest - 71, // 136: gctrpc.GoCryptoTraderService.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest - 73, // 137: gctrpc.GoCryptoTraderService.GetEvents:input_type -> gctrpc.GetEventsRequest - 76, // 138: gctrpc.GoCryptoTraderService.AddEvent:input_type -> gctrpc.AddEventRequest - 78, // 139: gctrpc.GoCryptoTraderService.RemoveEvent:input_type -> gctrpc.RemoveEventRequest - 79, // 140: gctrpc.GoCryptoTraderService.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest - 83, // 141: gctrpc.GoCryptoTraderService.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest - 85, // 142: gctrpc.GoCryptoTraderService.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest - 87, // 143: gctrpc.GoCryptoTraderService.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest - 88, // 144: gctrpc.GoCryptoTraderService.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest - 90, // 145: gctrpc.GoCryptoTraderService.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest - 92, // 146: gctrpc.GoCryptoTraderService.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest - 93, // 147: gctrpc.GoCryptoTraderService.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest - 100, // 148: gctrpc.GoCryptoTraderService.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest - 102, // 149: gctrpc.GoCryptoTraderService.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest - 103, // 150: gctrpc.GoCryptoTraderService.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest - 105, // 151: gctrpc.GoCryptoTraderService.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest - 106, // 152: gctrpc.GoCryptoTraderService.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest - 107, // 153: gctrpc.GoCryptoTraderService.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest - 108, // 154: gctrpc.GoCryptoTraderService.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest - 109, // 155: gctrpc.GoCryptoTraderService.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest - 110, // 156: gctrpc.GoCryptoTraderService.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest - 121, // 157: gctrpc.GoCryptoTraderService.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest - 126, // 158: gctrpc.GoCryptoTraderService.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest - 127, // 159: gctrpc.GoCryptoTraderService.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest - 124, // 160: gctrpc.GoCryptoTraderService.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest - 128, // 161: gctrpc.GoCryptoTraderService.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest - 122, // 162: gctrpc.GoCryptoTraderService.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest - 123, // 163: gctrpc.GoCryptoTraderService.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest - 125, // 164: gctrpc.GoCryptoTraderService.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest - 129, // 165: gctrpc.GoCryptoTraderService.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest - 116, // 166: gctrpc.GoCryptoTraderService.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest - 133, // 167: gctrpc.GoCryptoTraderService.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest - 134, // 168: gctrpc.GoCryptoTraderService.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest - 135, // 169: gctrpc.GoCryptoTraderService.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest - 136, // 170: gctrpc.GoCryptoTraderService.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest - 138, // 171: gctrpc.GoCryptoTraderService.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest - 140, // 172: gctrpc.GoCryptoTraderService.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest - 141, // 173: gctrpc.GoCryptoTraderService.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest - 144, // 174: gctrpc.GoCryptoTraderService.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest - 145, // 175: gctrpc.GoCryptoTraderService.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest - 112, // 176: gctrpc.GoCryptoTraderService.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest - 112, // 177: gctrpc.GoCryptoTraderService.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest - 112, // 178: gctrpc.GoCryptoTraderService.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest - 115, // 179: gctrpc.GoCryptoTraderService.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest - 146, // 180: gctrpc.GoCryptoTraderService.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest - 147, // 181: gctrpc.GoCryptoTraderService.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest - 149, // 182: gctrpc.GoCryptoTraderService.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest - 150, // 183: gctrpc.GoCryptoTraderService.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest - 154, // 184: gctrpc.GoCryptoTraderService.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 0, // 185: gctrpc.GoCryptoTraderService.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest - 158, // 186: gctrpc.GoCryptoTraderService.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest - 154, // 187: gctrpc.GoCryptoTraderService.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest - 159, // 188: gctrpc.GoCryptoTraderService.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest - 160, // 189: gctrpc.GoCryptoTraderService.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest - 58, // 190: gctrpc.GoCryptoTraderService.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest - 161, // 191: gctrpc.GoCryptoTraderService.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest - 163, // 192: gctrpc.GoCryptoTraderService.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest - 164, // 193: gctrpc.GoCryptoTraderService.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest - 167, // 194: gctrpc.GoCryptoTraderService.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest - 166, // 195: gctrpc.GoCryptoTraderService.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest - 165, // 196: gctrpc.GoCryptoTraderService.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest - 170, // 197: gctrpc.GoCryptoTraderService.GetFuturesPositions:input_type -> gctrpc.GetFuturesPositionsRequest - 173, // 198: gctrpc.GoCryptoTraderService.GetCollateral:input_type -> gctrpc.GetCollateralRequest - 178, // 199: gctrpc.GoCryptoTraderService.Shutdown:input_type -> gctrpc.ShutdownRequest - 1, // 200: gctrpc.GoCryptoTraderService.GetInfo:output_type -> gctrpc.GetInfoResponse - 7, // 201: gctrpc.GoCryptoTraderService.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse - 132, // 202: gctrpc.GoCryptoTraderService.EnableSubsystem:output_type -> gctrpc.GenericResponse - 132, // 203: gctrpc.GoCryptoTraderService.DisableSubsystem:output_type -> gctrpc.GenericResponse - 10, // 204: gctrpc.GoCryptoTraderService.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse - 4, // 205: gctrpc.GoCryptoTraderService.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse - 13, // 206: gctrpc.GoCryptoTraderService.GetExchanges:output_type -> gctrpc.GetExchangesResponse - 132, // 207: gctrpc.GoCryptoTraderService.DisableExchange:output_type -> gctrpc.GenericResponse - 19, // 208: gctrpc.GoCryptoTraderService.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse - 14, // 209: gctrpc.GoCryptoTraderService.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse - 16, // 210: gctrpc.GoCryptoTraderService.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse - 132, // 211: gctrpc.GoCryptoTraderService.EnableExchange:output_type -> gctrpc.GenericResponse - 22, // 212: gctrpc.GoCryptoTraderService.GetTicker:output_type -> gctrpc.TickerResponse - 25, // 213: gctrpc.GoCryptoTraderService.GetTickers:output_type -> gctrpc.GetTickersResponse - 28, // 214: gctrpc.GoCryptoTraderService.GetOrderbook:output_type -> gctrpc.OrderbookResponse - 31, // 215: gctrpc.GoCryptoTraderService.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse - 35, // 216: gctrpc.GoCryptoTraderService.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 217: gctrpc.GoCryptoTraderService.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse - 35, // 218: gctrpc.GoCryptoTraderService.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse - 37, // 219: gctrpc.GoCryptoTraderService.GetConfig:output_type -> gctrpc.GetConfigResponse - 40, // 220: gctrpc.GoCryptoTraderService.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse - 47, // 221: gctrpc.GoCryptoTraderService.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse - 132, // 222: gctrpc.GoCryptoTraderService.AddPortfolioAddress:output_type -> gctrpc.GenericResponse - 132, // 223: gctrpc.GoCryptoTraderService.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse - 52, // 224: gctrpc.GoCryptoTraderService.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse - 55, // 225: gctrpc.GoCryptoTraderService.GetForexRates:output_type -> gctrpc.GetForexRatesResponse - 59, // 226: gctrpc.GoCryptoTraderService.GetOrders:output_type -> gctrpc.GetOrdersResponse - 56, // 227: gctrpc.GoCryptoTraderService.GetOrder:output_type -> gctrpc.OrderDetails - 63, // 228: gctrpc.GoCryptoTraderService.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse - 65, // 229: gctrpc.GoCryptoTraderService.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse - 65, // 230: gctrpc.GoCryptoTraderService.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse - 132, // 231: gctrpc.GoCryptoTraderService.CancelOrder:output_type -> gctrpc.GenericResponse - 70, // 232: gctrpc.GoCryptoTraderService.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse - 72, // 233: gctrpc.GoCryptoTraderService.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse - 75, // 234: gctrpc.GoCryptoTraderService.GetEvents:output_type -> gctrpc.GetEventsResponse - 77, // 235: gctrpc.GoCryptoTraderService.AddEvent:output_type -> gctrpc.AddEventResponse - 132, // 236: gctrpc.GoCryptoTraderService.RemoveEvent:output_type -> gctrpc.GenericResponse - 82, // 237: gctrpc.GoCryptoTraderService.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse - 84, // 238: gctrpc.GoCryptoTraderService.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse - 86, // 239: gctrpc.GoCryptoTraderService.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse - 89, // 240: gctrpc.GoCryptoTraderService.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse - 89, // 241: gctrpc.GoCryptoTraderService.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse - 91, // 242: gctrpc.GoCryptoTraderService.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse - 94, // 243: gctrpc.GoCryptoTraderService.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 94, // 244: gctrpc.GoCryptoTraderService.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse - 101, // 245: gctrpc.GoCryptoTraderService.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 101, // 246: gctrpc.GoCryptoTraderService.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse - 104, // 247: gctrpc.GoCryptoTraderService.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse - 132, // 248: gctrpc.GoCryptoTraderService.SetExchangePair:output_type -> gctrpc.GenericResponse - 28, // 249: gctrpc.GoCryptoTraderService.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse - 28, // 250: gctrpc.GoCryptoTraderService.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse - 22, // 251: gctrpc.GoCryptoTraderService.GetTickerStream:output_type -> gctrpc.TickerResponse - 22, // 252: gctrpc.GoCryptoTraderService.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse - 111, // 253: gctrpc.GoCryptoTraderService.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse - 132, // 254: gctrpc.GoCryptoTraderService.GCTScriptExecute:output_type -> gctrpc.GenericResponse - 132, // 255: gctrpc.GoCryptoTraderService.GCTScriptUpload:output_type -> gctrpc.GenericResponse - 131, // 256: gctrpc.GoCryptoTraderService.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse - 130, // 257: gctrpc.GoCryptoTraderService.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse - 131, // 258: gctrpc.GoCryptoTraderService.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse - 132, // 259: gctrpc.GoCryptoTraderService.GCTScriptStop:output_type -> gctrpc.GenericResponse - 132, // 260: gctrpc.GoCryptoTraderService.GCTScriptStopAll:output_type -> gctrpc.GenericResponse - 130, // 261: gctrpc.GoCryptoTraderService.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse - 132, // 262: gctrpc.GoCryptoTraderService.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse - 117, // 263: gctrpc.GoCryptoTraderService.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 132, // 264: gctrpc.GoCryptoTraderService.SetExchangeAsset:output_type -> gctrpc.GenericResponse - 132, // 265: gctrpc.GoCryptoTraderService.SetAllExchangePairs:output_type -> gctrpc.GenericResponse - 132, // 266: gctrpc.GoCryptoTraderService.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse - 137, // 267: gctrpc.GoCryptoTraderService.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse - 139, // 268: gctrpc.GoCryptoTraderService.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse - 132, // 269: gctrpc.GoCryptoTraderService.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse - 143, // 270: gctrpc.GoCryptoTraderService.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse - 132, // 271: gctrpc.GoCryptoTraderService.WebsocketSetProxy:output_type -> gctrpc.GenericResponse - 132, // 272: gctrpc.GoCryptoTraderService.WebsocketSetURL:output_type -> gctrpc.GenericResponse - 114, // 273: gctrpc.GoCryptoTraderService.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse - 114, // 274: gctrpc.GoCryptoTraderService.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse - 114, // 275: gctrpc.GoCryptoTraderService.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse - 117, // 276: gctrpc.GoCryptoTraderService.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse - 148, // 277: gctrpc.GoCryptoTraderService.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 148, // 278: gctrpc.GoCryptoTraderService.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse - 132, // 279: gctrpc.GoCryptoTraderService.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse - 153, // 280: gctrpc.GoCryptoTraderService.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse - 155, // 281: gctrpc.GoCryptoTraderService.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob - 157, // 282: gctrpc.GoCryptoTraderService.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs - 157, // 283: gctrpc.GoCryptoTraderService.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs - 155, // 284: gctrpc.GoCryptoTraderService.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob - 132, // 285: gctrpc.GoCryptoTraderService.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse - 132, // 286: gctrpc.GoCryptoTraderService.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse - 59, // 287: gctrpc.GoCryptoTraderService.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse - 162, // 288: gctrpc.GoCryptoTraderService.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse - 168, // 289: gctrpc.GoCryptoTraderService.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse - 132, // 290: gctrpc.GoCryptoTraderService.CurrencyStateTrading:output_type -> gctrpc.GenericResponse - 132, // 291: gctrpc.GoCryptoTraderService.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse - 132, // 292: gctrpc.GoCryptoTraderService.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse - 132, // 293: gctrpc.GoCryptoTraderService.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse - 171, // 294: gctrpc.GoCryptoTraderService.GetFuturesPositions:output_type -> gctrpc.GetFuturesPositionsResponse - 174, // 295: gctrpc.GoCryptoTraderService.GetCollateral:output_type -> gctrpc.GetCollateralResponse - 179, // 296: gctrpc.GoCryptoTraderService.Shutdown:output_type -> gctrpc.ShutdownResponse - 200, // [200:297] is the sub-list for method output_type - 103, // [103:200] is the sub-list for method input_type - 103, // [103:103] is the sub-list for extension type_name - 103, // [103:103] is the sub-list for extension extendee - 0, // [0:103] is the sub-list for field type_name + 21, // 94: gctrpc.GetTechnicalAnalysisRequest.pair:type_name -> gctrpc.CurrencyPair + 197, // 95: gctrpc.GetTechnicalAnalysisRequest.start:type_name -> google.protobuf.Timestamp + 197, // 96: gctrpc.GetTechnicalAnalysisRequest.end:type_name -> google.protobuf.Timestamp + 21, // 97: gctrpc.GetTechnicalAnalysisRequest.other_pair:type_name -> gctrpc.CurrencyPair + 196, // 98: gctrpc.GetTechnicalAnalysisResponse.signals:type_name -> gctrpc.GetTechnicalAnalysisResponse.SignalsEntry + 9, // 99: gctrpc.GetInfoResponse.RpcEndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 3, // 100: gctrpc.GetCommunicationRelayersResponse.CommunicationRelayersEntry.value:type_name -> gctrpc.CommunicationRelayer + 9, // 101: gctrpc.GetRPCEndpointsResponse.EndpointsEntry.value:type_name -> gctrpc.RPCEndpoint + 18, // 102: gctrpc.GetExchangeInfoResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 44, // 103: gctrpc.OnlineCoins.CoinsEntry.value:type_name -> gctrpc.OnlineCoinSummary + 45, // 104: gctrpc.GetPortfolioSummaryResponse.CoinsOfflineSummaryEntry.value:type_name -> gctrpc.OfflineCoins + 46, // 105: gctrpc.GetPortfolioSummaryResponse.CoinsOnlineSummaryEntry.value:type_name -> gctrpc.OnlineCoins + 81, // 106: gctrpc.GetCryptocurrencyDepositAddressesResponse.AddressesEntry.value:type_name -> gctrpc.DepositAddresses + 18, // 107: gctrpc.GetExchangePairsResponse.SupportedAssetsEntry.value:type_name -> gctrpc.PairsSupported + 181, // 108: gctrpc.GetTechnicalAnalysisResponse.SignalsEntry.value:type_name -> gctrpc.ListOfSignals + 0, // 109: gctrpc.GoCryptoTraderService.GetInfo:input_type -> gctrpc.GetInfoRequest + 6, // 110: gctrpc.GoCryptoTraderService.GetSubsystems:input_type -> gctrpc.GetSubsystemsRequest + 5, // 111: gctrpc.GoCryptoTraderService.EnableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 5, // 112: gctrpc.GoCryptoTraderService.DisableSubsystem:input_type -> gctrpc.GenericSubsystemRequest + 8, // 113: gctrpc.GoCryptoTraderService.GetRPCEndpoints:input_type -> gctrpc.GetRPCEndpointsRequest + 2, // 114: gctrpc.GoCryptoTraderService.GetCommunicationRelayers:input_type -> gctrpc.GetCommunicationRelayersRequest + 12, // 115: gctrpc.GoCryptoTraderService.GetExchanges:input_type -> gctrpc.GetExchangesRequest + 11, // 116: gctrpc.GoCryptoTraderService.DisableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 117: gctrpc.GoCryptoTraderService.GetExchangeInfo:input_type -> gctrpc.GenericExchangeNameRequest + 11, // 118: gctrpc.GoCryptoTraderService.GetExchangeOTPCode:input_type -> gctrpc.GenericExchangeNameRequest + 15, // 119: gctrpc.GoCryptoTraderService.GetExchangeOTPCodes:input_type -> gctrpc.GetExchangeOTPsRequest + 11, // 120: gctrpc.GoCryptoTraderService.EnableExchange:input_type -> gctrpc.GenericExchangeNameRequest + 20, // 121: gctrpc.GoCryptoTraderService.GetTicker:input_type -> gctrpc.GetTickerRequest + 23, // 122: gctrpc.GoCryptoTraderService.GetTickers:input_type -> gctrpc.GetTickersRequest + 26, // 123: gctrpc.GoCryptoTraderService.GetOrderbook:input_type -> gctrpc.GetOrderbookRequest + 29, // 124: gctrpc.GoCryptoTraderService.GetOrderbooks:input_type -> gctrpc.GetOrderbooksRequest + 32, // 125: gctrpc.GoCryptoTraderService.GetAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 126: gctrpc.GoCryptoTraderService.UpdateAccountInfo:input_type -> gctrpc.GetAccountInfoRequest + 32, // 127: gctrpc.GoCryptoTraderService.GetAccountInfoStream:input_type -> gctrpc.GetAccountInfoRequest + 36, // 128: gctrpc.GoCryptoTraderService.GetConfig:input_type -> gctrpc.GetConfigRequest + 39, // 129: gctrpc.GoCryptoTraderService.GetPortfolio:input_type -> gctrpc.GetPortfolioRequest + 41, // 130: gctrpc.GoCryptoTraderService.GetPortfolioSummary:input_type -> gctrpc.GetPortfolioSummaryRequest + 48, // 131: gctrpc.GoCryptoTraderService.AddPortfolioAddress:input_type -> gctrpc.AddPortfolioAddressRequest + 49, // 132: gctrpc.GoCryptoTraderService.RemovePortfolioAddress:input_type -> gctrpc.RemovePortfolioAddressRequest + 50, // 133: gctrpc.GoCryptoTraderService.GetForexProviders:input_type -> gctrpc.GetForexProvidersRequest + 53, // 134: gctrpc.GoCryptoTraderService.GetForexRates:input_type -> gctrpc.GetForexRatesRequest + 58, // 135: gctrpc.GoCryptoTraderService.GetOrders:input_type -> gctrpc.GetOrdersRequest + 60, // 136: gctrpc.GoCryptoTraderService.GetOrder:input_type -> gctrpc.GetOrderRequest + 61, // 137: gctrpc.GoCryptoTraderService.SubmitOrder:input_type -> gctrpc.SubmitOrderRequest + 64, // 138: gctrpc.GoCryptoTraderService.SimulateOrder:input_type -> gctrpc.SimulateOrderRequest + 66, // 139: gctrpc.GoCryptoTraderService.WhaleBomb:input_type -> gctrpc.WhaleBombRequest + 67, // 140: gctrpc.GoCryptoTraderService.CancelOrder:input_type -> gctrpc.CancelOrderRequest + 68, // 141: gctrpc.GoCryptoTraderService.CancelBatchOrders:input_type -> gctrpc.CancelBatchOrdersRequest + 71, // 142: gctrpc.GoCryptoTraderService.CancelAllOrders:input_type -> gctrpc.CancelAllOrdersRequest + 73, // 143: gctrpc.GoCryptoTraderService.GetEvents:input_type -> gctrpc.GetEventsRequest + 76, // 144: gctrpc.GoCryptoTraderService.AddEvent:input_type -> gctrpc.AddEventRequest + 78, // 145: gctrpc.GoCryptoTraderService.RemoveEvent:input_type -> gctrpc.RemoveEventRequest + 79, // 146: gctrpc.GoCryptoTraderService.GetCryptocurrencyDepositAddresses:input_type -> gctrpc.GetCryptocurrencyDepositAddressesRequest + 83, // 147: gctrpc.GoCryptoTraderService.GetCryptocurrencyDepositAddress:input_type -> gctrpc.GetCryptocurrencyDepositAddressRequest + 85, // 148: gctrpc.GoCryptoTraderService.GetAvailableTransferChains:input_type -> gctrpc.GetAvailableTransferChainsRequest + 87, // 149: gctrpc.GoCryptoTraderService.WithdrawFiatFunds:input_type -> gctrpc.WithdrawFiatRequest + 88, // 150: gctrpc.GoCryptoTraderService.WithdrawCryptocurrencyFunds:input_type -> gctrpc.WithdrawCryptoRequest + 90, // 151: gctrpc.GoCryptoTraderService.WithdrawalEventByID:input_type -> gctrpc.WithdrawalEventByIDRequest + 92, // 152: gctrpc.GoCryptoTraderService.WithdrawalEventsByExchange:input_type -> gctrpc.WithdrawalEventsByExchangeRequest + 93, // 153: gctrpc.GoCryptoTraderService.WithdrawalEventsByDate:input_type -> gctrpc.WithdrawalEventsByDateRequest + 100, // 154: gctrpc.GoCryptoTraderService.GetLoggerDetails:input_type -> gctrpc.GetLoggerDetailsRequest + 102, // 155: gctrpc.GoCryptoTraderService.SetLoggerDetails:input_type -> gctrpc.SetLoggerDetailsRequest + 103, // 156: gctrpc.GoCryptoTraderService.GetExchangePairs:input_type -> gctrpc.GetExchangePairsRequest + 105, // 157: gctrpc.GoCryptoTraderService.SetExchangePair:input_type -> gctrpc.SetExchangePairRequest + 106, // 158: gctrpc.GoCryptoTraderService.GetOrderbookStream:input_type -> gctrpc.GetOrderbookStreamRequest + 107, // 159: gctrpc.GoCryptoTraderService.GetExchangeOrderbookStream:input_type -> gctrpc.GetExchangeOrderbookStreamRequest + 108, // 160: gctrpc.GoCryptoTraderService.GetTickerStream:input_type -> gctrpc.GetTickerStreamRequest + 109, // 161: gctrpc.GoCryptoTraderService.GetExchangeTickerStream:input_type -> gctrpc.GetExchangeTickerStreamRequest + 110, // 162: gctrpc.GoCryptoTraderService.GetAuditEvent:input_type -> gctrpc.GetAuditEventRequest + 121, // 163: gctrpc.GoCryptoTraderService.GCTScriptExecute:input_type -> gctrpc.GCTScriptExecuteRequest + 126, // 164: gctrpc.GoCryptoTraderService.GCTScriptUpload:input_type -> gctrpc.GCTScriptUploadRequest + 127, // 165: gctrpc.GoCryptoTraderService.GCTScriptReadScript:input_type -> gctrpc.GCTScriptReadScriptRequest + 124, // 166: gctrpc.GoCryptoTraderService.GCTScriptStatus:input_type -> gctrpc.GCTScriptStatusRequest + 128, // 167: gctrpc.GoCryptoTraderService.GCTScriptQuery:input_type -> gctrpc.GCTScriptQueryRequest + 122, // 168: gctrpc.GoCryptoTraderService.GCTScriptStop:input_type -> gctrpc.GCTScriptStopRequest + 123, // 169: gctrpc.GoCryptoTraderService.GCTScriptStopAll:input_type -> gctrpc.GCTScriptStopAllRequest + 125, // 170: gctrpc.GoCryptoTraderService.GCTScriptListAll:input_type -> gctrpc.GCTScriptListAllRequest + 129, // 171: gctrpc.GoCryptoTraderService.GCTScriptAutoLoadToggle:input_type -> gctrpc.GCTScriptAutoLoadRequest + 116, // 172: gctrpc.GoCryptoTraderService.GetHistoricCandles:input_type -> gctrpc.GetHistoricCandlesRequest + 133, // 173: gctrpc.GoCryptoTraderService.SetExchangeAsset:input_type -> gctrpc.SetExchangeAssetRequest + 134, // 174: gctrpc.GoCryptoTraderService.SetAllExchangePairs:input_type -> gctrpc.SetExchangeAllPairsRequest + 135, // 175: gctrpc.GoCryptoTraderService.UpdateExchangeSupportedPairs:input_type -> gctrpc.UpdateExchangeSupportedPairsRequest + 136, // 176: gctrpc.GoCryptoTraderService.GetExchangeAssets:input_type -> gctrpc.GetExchangeAssetsRequest + 138, // 177: gctrpc.GoCryptoTraderService.WebsocketGetInfo:input_type -> gctrpc.WebsocketGetInfoRequest + 140, // 178: gctrpc.GoCryptoTraderService.WebsocketSetEnabled:input_type -> gctrpc.WebsocketSetEnabledRequest + 141, // 179: gctrpc.GoCryptoTraderService.WebsocketGetSubscriptions:input_type -> gctrpc.WebsocketGetSubscriptionsRequest + 144, // 180: gctrpc.GoCryptoTraderService.WebsocketSetProxy:input_type -> gctrpc.WebsocketSetProxyRequest + 145, // 181: gctrpc.GoCryptoTraderService.WebsocketSetURL:input_type -> gctrpc.WebsocketSetURLRequest + 112, // 182: gctrpc.GoCryptoTraderService.GetRecentTrades:input_type -> gctrpc.GetSavedTradesRequest + 112, // 183: gctrpc.GoCryptoTraderService.GetHistoricTrades:input_type -> gctrpc.GetSavedTradesRequest + 112, // 184: gctrpc.GoCryptoTraderService.GetSavedTrades:input_type -> gctrpc.GetSavedTradesRequest + 115, // 185: gctrpc.GoCryptoTraderService.ConvertTradesToCandles:input_type -> gctrpc.ConvertTradesToCandlesRequest + 146, // 186: gctrpc.GoCryptoTraderService.FindMissingSavedCandleIntervals:input_type -> gctrpc.FindMissingCandlePeriodsRequest + 147, // 187: gctrpc.GoCryptoTraderService.FindMissingSavedTradeIntervals:input_type -> gctrpc.FindMissingTradePeriodsRequest + 149, // 188: gctrpc.GoCryptoTraderService.SetExchangeTradeProcessing:input_type -> gctrpc.SetExchangeTradeProcessingRequest + 150, // 189: gctrpc.GoCryptoTraderService.UpsertDataHistoryJob:input_type -> gctrpc.UpsertDataHistoryJobRequest + 154, // 190: gctrpc.GoCryptoTraderService.GetDataHistoryJobDetails:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 0, // 191: gctrpc.GoCryptoTraderService.GetActiveDataHistoryJobs:input_type -> gctrpc.GetInfoRequest + 158, // 192: gctrpc.GoCryptoTraderService.GetDataHistoryJobsBetween:input_type -> gctrpc.GetDataHistoryJobsBetweenRequest + 154, // 193: gctrpc.GoCryptoTraderService.GetDataHistoryJobSummary:input_type -> gctrpc.GetDataHistoryJobDetailsRequest + 159, // 194: gctrpc.GoCryptoTraderService.SetDataHistoryJobStatus:input_type -> gctrpc.SetDataHistoryJobStatusRequest + 160, // 195: gctrpc.GoCryptoTraderService.UpdateDataHistoryJobPrerequisite:input_type -> gctrpc.UpdateDataHistoryJobPrerequisiteRequest + 58, // 196: gctrpc.GoCryptoTraderService.GetManagedOrders:input_type -> gctrpc.GetOrdersRequest + 161, // 197: gctrpc.GoCryptoTraderService.ModifyOrder:input_type -> gctrpc.ModifyOrderRequest + 163, // 198: gctrpc.GoCryptoTraderService.CurrencyStateGetAll:input_type -> gctrpc.CurrencyStateGetAllRequest + 164, // 199: gctrpc.GoCryptoTraderService.CurrencyStateTrading:input_type -> gctrpc.CurrencyStateTradingRequest + 167, // 200: gctrpc.GoCryptoTraderService.CurrencyStateDeposit:input_type -> gctrpc.CurrencyStateDepositRequest + 166, // 201: gctrpc.GoCryptoTraderService.CurrencyStateWithdraw:input_type -> gctrpc.CurrencyStateWithdrawRequest + 165, // 202: gctrpc.GoCryptoTraderService.CurrencyStateTradingPair:input_type -> gctrpc.CurrencyStateTradingPairRequest + 170, // 203: gctrpc.GoCryptoTraderService.GetFuturesPositions:input_type -> gctrpc.GetFuturesPositionsRequest + 173, // 204: gctrpc.GoCryptoTraderService.GetCollateral:input_type -> gctrpc.GetCollateralRequest + 178, // 205: gctrpc.GoCryptoTraderService.Shutdown:input_type -> gctrpc.ShutdownRequest + 180, // 206: gctrpc.GoCryptoTraderService.GetTechnicalAnalysis:input_type -> gctrpc.GetTechnicalAnalysisRequest + 1, // 207: gctrpc.GoCryptoTraderService.GetInfo:output_type -> gctrpc.GetInfoResponse + 7, // 208: gctrpc.GoCryptoTraderService.GetSubsystems:output_type -> gctrpc.GetSusbsytemsResponse + 132, // 209: gctrpc.GoCryptoTraderService.EnableSubsystem:output_type -> gctrpc.GenericResponse + 132, // 210: gctrpc.GoCryptoTraderService.DisableSubsystem:output_type -> gctrpc.GenericResponse + 10, // 211: gctrpc.GoCryptoTraderService.GetRPCEndpoints:output_type -> gctrpc.GetRPCEndpointsResponse + 4, // 212: gctrpc.GoCryptoTraderService.GetCommunicationRelayers:output_type -> gctrpc.GetCommunicationRelayersResponse + 13, // 213: gctrpc.GoCryptoTraderService.GetExchanges:output_type -> gctrpc.GetExchangesResponse + 132, // 214: gctrpc.GoCryptoTraderService.DisableExchange:output_type -> gctrpc.GenericResponse + 19, // 215: gctrpc.GoCryptoTraderService.GetExchangeInfo:output_type -> gctrpc.GetExchangeInfoResponse + 14, // 216: gctrpc.GoCryptoTraderService.GetExchangeOTPCode:output_type -> gctrpc.GetExchangeOTPResponse + 16, // 217: gctrpc.GoCryptoTraderService.GetExchangeOTPCodes:output_type -> gctrpc.GetExchangeOTPsResponse + 132, // 218: gctrpc.GoCryptoTraderService.EnableExchange:output_type -> gctrpc.GenericResponse + 22, // 219: gctrpc.GoCryptoTraderService.GetTicker:output_type -> gctrpc.TickerResponse + 25, // 220: gctrpc.GoCryptoTraderService.GetTickers:output_type -> gctrpc.GetTickersResponse + 28, // 221: gctrpc.GoCryptoTraderService.GetOrderbook:output_type -> gctrpc.OrderbookResponse + 31, // 222: gctrpc.GoCryptoTraderService.GetOrderbooks:output_type -> gctrpc.GetOrderbooksResponse + 35, // 223: gctrpc.GoCryptoTraderService.GetAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 224: gctrpc.GoCryptoTraderService.UpdateAccountInfo:output_type -> gctrpc.GetAccountInfoResponse + 35, // 225: gctrpc.GoCryptoTraderService.GetAccountInfoStream:output_type -> gctrpc.GetAccountInfoResponse + 37, // 226: gctrpc.GoCryptoTraderService.GetConfig:output_type -> gctrpc.GetConfigResponse + 40, // 227: gctrpc.GoCryptoTraderService.GetPortfolio:output_type -> gctrpc.GetPortfolioResponse + 47, // 228: gctrpc.GoCryptoTraderService.GetPortfolioSummary:output_type -> gctrpc.GetPortfolioSummaryResponse + 132, // 229: gctrpc.GoCryptoTraderService.AddPortfolioAddress:output_type -> gctrpc.GenericResponse + 132, // 230: gctrpc.GoCryptoTraderService.RemovePortfolioAddress:output_type -> gctrpc.GenericResponse + 52, // 231: gctrpc.GoCryptoTraderService.GetForexProviders:output_type -> gctrpc.GetForexProvidersResponse + 55, // 232: gctrpc.GoCryptoTraderService.GetForexRates:output_type -> gctrpc.GetForexRatesResponse + 59, // 233: gctrpc.GoCryptoTraderService.GetOrders:output_type -> gctrpc.GetOrdersResponse + 56, // 234: gctrpc.GoCryptoTraderService.GetOrder:output_type -> gctrpc.OrderDetails + 63, // 235: gctrpc.GoCryptoTraderService.SubmitOrder:output_type -> gctrpc.SubmitOrderResponse + 65, // 236: gctrpc.GoCryptoTraderService.SimulateOrder:output_type -> gctrpc.SimulateOrderResponse + 65, // 237: gctrpc.GoCryptoTraderService.WhaleBomb:output_type -> gctrpc.SimulateOrderResponse + 132, // 238: gctrpc.GoCryptoTraderService.CancelOrder:output_type -> gctrpc.GenericResponse + 70, // 239: gctrpc.GoCryptoTraderService.CancelBatchOrders:output_type -> gctrpc.CancelBatchOrdersResponse + 72, // 240: gctrpc.GoCryptoTraderService.CancelAllOrders:output_type -> gctrpc.CancelAllOrdersResponse + 75, // 241: gctrpc.GoCryptoTraderService.GetEvents:output_type -> gctrpc.GetEventsResponse + 77, // 242: gctrpc.GoCryptoTraderService.AddEvent:output_type -> gctrpc.AddEventResponse + 132, // 243: gctrpc.GoCryptoTraderService.RemoveEvent:output_type -> gctrpc.GenericResponse + 82, // 244: gctrpc.GoCryptoTraderService.GetCryptocurrencyDepositAddresses:output_type -> gctrpc.GetCryptocurrencyDepositAddressesResponse + 84, // 245: gctrpc.GoCryptoTraderService.GetCryptocurrencyDepositAddress:output_type -> gctrpc.GetCryptocurrencyDepositAddressResponse + 86, // 246: gctrpc.GoCryptoTraderService.GetAvailableTransferChains:output_type -> gctrpc.GetAvailableTransferChainsResponse + 89, // 247: gctrpc.GoCryptoTraderService.WithdrawFiatFunds:output_type -> gctrpc.WithdrawResponse + 89, // 248: gctrpc.GoCryptoTraderService.WithdrawCryptocurrencyFunds:output_type -> gctrpc.WithdrawResponse + 91, // 249: gctrpc.GoCryptoTraderService.WithdrawalEventByID:output_type -> gctrpc.WithdrawalEventByIDResponse + 94, // 250: gctrpc.GoCryptoTraderService.WithdrawalEventsByExchange:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 94, // 251: gctrpc.GoCryptoTraderService.WithdrawalEventsByDate:output_type -> gctrpc.WithdrawalEventsByExchangeResponse + 101, // 252: gctrpc.GoCryptoTraderService.GetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 101, // 253: gctrpc.GoCryptoTraderService.SetLoggerDetails:output_type -> gctrpc.GetLoggerDetailsResponse + 104, // 254: gctrpc.GoCryptoTraderService.GetExchangePairs:output_type -> gctrpc.GetExchangePairsResponse + 132, // 255: gctrpc.GoCryptoTraderService.SetExchangePair:output_type -> gctrpc.GenericResponse + 28, // 256: gctrpc.GoCryptoTraderService.GetOrderbookStream:output_type -> gctrpc.OrderbookResponse + 28, // 257: gctrpc.GoCryptoTraderService.GetExchangeOrderbookStream:output_type -> gctrpc.OrderbookResponse + 22, // 258: gctrpc.GoCryptoTraderService.GetTickerStream:output_type -> gctrpc.TickerResponse + 22, // 259: gctrpc.GoCryptoTraderService.GetExchangeTickerStream:output_type -> gctrpc.TickerResponse + 111, // 260: gctrpc.GoCryptoTraderService.GetAuditEvent:output_type -> gctrpc.GetAuditEventResponse + 132, // 261: gctrpc.GoCryptoTraderService.GCTScriptExecute:output_type -> gctrpc.GenericResponse + 132, // 262: gctrpc.GoCryptoTraderService.GCTScriptUpload:output_type -> gctrpc.GenericResponse + 131, // 263: gctrpc.GoCryptoTraderService.GCTScriptReadScript:output_type -> gctrpc.GCTScriptQueryResponse + 130, // 264: gctrpc.GoCryptoTraderService.GCTScriptStatus:output_type -> gctrpc.GCTScriptStatusResponse + 131, // 265: gctrpc.GoCryptoTraderService.GCTScriptQuery:output_type -> gctrpc.GCTScriptQueryResponse + 132, // 266: gctrpc.GoCryptoTraderService.GCTScriptStop:output_type -> gctrpc.GenericResponse + 132, // 267: gctrpc.GoCryptoTraderService.GCTScriptStopAll:output_type -> gctrpc.GenericResponse + 130, // 268: gctrpc.GoCryptoTraderService.GCTScriptListAll:output_type -> gctrpc.GCTScriptStatusResponse + 132, // 269: gctrpc.GoCryptoTraderService.GCTScriptAutoLoadToggle:output_type -> gctrpc.GenericResponse + 117, // 270: gctrpc.GoCryptoTraderService.GetHistoricCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 132, // 271: gctrpc.GoCryptoTraderService.SetExchangeAsset:output_type -> gctrpc.GenericResponse + 132, // 272: gctrpc.GoCryptoTraderService.SetAllExchangePairs:output_type -> gctrpc.GenericResponse + 132, // 273: gctrpc.GoCryptoTraderService.UpdateExchangeSupportedPairs:output_type -> gctrpc.GenericResponse + 137, // 274: gctrpc.GoCryptoTraderService.GetExchangeAssets:output_type -> gctrpc.GetExchangeAssetsResponse + 139, // 275: gctrpc.GoCryptoTraderService.WebsocketGetInfo:output_type -> gctrpc.WebsocketGetInfoResponse + 132, // 276: gctrpc.GoCryptoTraderService.WebsocketSetEnabled:output_type -> gctrpc.GenericResponse + 143, // 277: gctrpc.GoCryptoTraderService.WebsocketGetSubscriptions:output_type -> gctrpc.WebsocketGetSubscriptionsResponse + 132, // 278: gctrpc.GoCryptoTraderService.WebsocketSetProxy:output_type -> gctrpc.GenericResponse + 132, // 279: gctrpc.GoCryptoTraderService.WebsocketSetURL:output_type -> gctrpc.GenericResponse + 114, // 280: gctrpc.GoCryptoTraderService.GetRecentTrades:output_type -> gctrpc.SavedTradesResponse + 114, // 281: gctrpc.GoCryptoTraderService.GetHistoricTrades:output_type -> gctrpc.SavedTradesResponse + 114, // 282: gctrpc.GoCryptoTraderService.GetSavedTrades:output_type -> gctrpc.SavedTradesResponse + 117, // 283: gctrpc.GoCryptoTraderService.ConvertTradesToCandles:output_type -> gctrpc.GetHistoricCandlesResponse + 148, // 284: gctrpc.GoCryptoTraderService.FindMissingSavedCandleIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 148, // 285: gctrpc.GoCryptoTraderService.FindMissingSavedTradeIntervals:output_type -> gctrpc.FindMissingIntervalsResponse + 132, // 286: gctrpc.GoCryptoTraderService.SetExchangeTradeProcessing:output_type -> gctrpc.GenericResponse + 153, // 287: gctrpc.GoCryptoTraderService.UpsertDataHistoryJob:output_type -> gctrpc.UpsertDataHistoryJobResponse + 155, // 288: gctrpc.GoCryptoTraderService.GetDataHistoryJobDetails:output_type -> gctrpc.DataHistoryJob + 157, // 289: gctrpc.GoCryptoTraderService.GetActiveDataHistoryJobs:output_type -> gctrpc.DataHistoryJobs + 157, // 290: gctrpc.GoCryptoTraderService.GetDataHistoryJobsBetween:output_type -> gctrpc.DataHistoryJobs + 155, // 291: gctrpc.GoCryptoTraderService.GetDataHistoryJobSummary:output_type -> gctrpc.DataHistoryJob + 132, // 292: gctrpc.GoCryptoTraderService.SetDataHistoryJobStatus:output_type -> gctrpc.GenericResponse + 132, // 293: gctrpc.GoCryptoTraderService.UpdateDataHistoryJobPrerequisite:output_type -> gctrpc.GenericResponse + 59, // 294: gctrpc.GoCryptoTraderService.GetManagedOrders:output_type -> gctrpc.GetOrdersResponse + 162, // 295: gctrpc.GoCryptoTraderService.ModifyOrder:output_type -> gctrpc.ModifyOrderResponse + 168, // 296: gctrpc.GoCryptoTraderService.CurrencyStateGetAll:output_type -> gctrpc.CurrencyStateResponse + 132, // 297: gctrpc.GoCryptoTraderService.CurrencyStateTrading:output_type -> gctrpc.GenericResponse + 132, // 298: gctrpc.GoCryptoTraderService.CurrencyStateDeposit:output_type -> gctrpc.GenericResponse + 132, // 299: gctrpc.GoCryptoTraderService.CurrencyStateWithdraw:output_type -> gctrpc.GenericResponse + 132, // 300: gctrpc.GoCryptoTraderService.CurrencyStateTradingPair:output_type -> gctrpc.GenericResponse + 171, // 301: gctrpc.GoCryptoTraderService.GetFuturesPositions:output_type -> gctrpc.GetFuturesPositionsResponse + 174, // 302: gctrpc.GoCryptoTraderService.GetCollateral:output_type -> gctrpc.GetCollateralResponse + 179, // 303: gctrpc.GoCryptoTraderService.Shutdown:output_type -> gctrpc.ShutdownResponse + 182, // 304: gctrpc.GoCryptoTraderService.GetTechnicalAnalysis:output_type -> gctrpc.GetTechnicalAnalysisResponse + 207, // [207:305] is the sub-list for method output_type + 109, // [109:207] is the sub-list for method input_type + 109, // [109:109] is the sub-list for extension type_name + 109, // [109:109] is the sub-list for extension extendee + 0, // [0:109] is the sub-list for field type_name } func init() { file_rpc_proto_init() } @@ -16801,6 +17139,42 @@ func file_rpc_proto_init() { return nil } } + file_rpc_proto_msgTypes[180].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTechnicalAnalysisRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[181].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListOfSignals); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rpc_proto_msgTypes[182].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTechnicalAnalysisResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -16808,7 +17182,7 @@ func file_rpc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rpc_proto_rawDesc, NumEnums: 0, - NumMessages: 193, + NumMessages: 197, NumExtensions: 0, NumServices: 1, }, diff --git a/gctrpc/rpc.pb.gw.go b/gctrpc/rpc.pb.gw.go index 136aafca..dd72ddf2 100644 --- a/gctrpc/rpc.pb.gw.go +++ b/gctrpc/rpc.pb.gw.go @@ -3113,6 +3113,42 @@ func local_request_GoCryptoTraderService_Shutdown_0(ctx context.Context, marshal } +var ( + filter_GoCryptoTraderService_GetTechnicalAnalysis_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_GoCryptoTraderService_GetTechnicalAnalysis_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTechnicalAnalysisRequest + 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_GoCryptoTraderService_GetTechnicalAnalysis_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetTechnicalAnalysis(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_GoCryptoTraderService_GetTechnicalAnalysis_0(ctx context.Context, marshaler runtime.Marshaler, server GoCryptoTraderServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GetTechnicalAnalysisRequest + 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_GoCryptoTraderService_GetTechnicalAnalysis_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.GetTechnicalAnalysis(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterGoCryptoTraderServiceHandlerServer registers the http handlers for service GoCryptoTraderService to "mux". // UnaryRPC :call GoCryptoTraderServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -5345,6 +5381,30 @@ func RegisterGoCryptoTraderServiceHandlerServer(ctx context.Context, mux *runtim }) + mux.Handle("GET", pattern_GoCryptoTraderService_GetTechnicalAnalysis_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + ctx, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/gctrpc.GoCryptoTraderService/GetTechnicalAnalysis", runtime.WithHTTPPathPattern("/v1/gettechnicalanalysis")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_GoCryptoTraderService_GetTechnicalAnalysis_0(ctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTraderService_GetTechnicalAnalysis_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -7423,6 +7483,27 @@ func RegisterGoCryptoTraderServiceHandlerClient(ctx context.Context, mux *runtim }) + mux.Handle("GET", pattern_GoCryptoTraderService_GetTechnicalAnalysis_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) + var err error + ctx, err = runtime.AnnotateContext(ctx, mux, req, "/gctrpc.GoCryptoTraderService/GetTechnicalAnalysis", runtime.WithHTTPPathPattern("/v1/gettechnicalanalysis")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_GoCryptoTraderService_GetTechnicalAnalysis_0(ctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_GoCryptoTraderService_GetTechnicalAnalysis_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -7620,6 +7701,8 @@ var ( pattern_GoCryptoTraderService_GetCollateral_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getcollateral"}, "")) pattern_GoCryptoTraderService_Shutdown_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "shutdown"}, "")) + + pattern_GoCryptoTraderService_GetTechnicalAnalysis_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "gettechnicalanalysis"}, "")) ) var ( @@ -7816,4 +7899,6 @@ var ( forward_GoCryptoTraderService_GetCollateral_0 = runtime.ForwardResponseMessage forward_GoCryptoTraderService_Shutdown_0 = runtime.ForwardResponseMessage + + forward_GoCryptoTraderService_GetTechnicalAnalysis_0 = runtime.ForwardResponseMessage ) diff --git a/gctrpc/rpc.proto b/gctrpc/rpc.proto index 2f345f78..bd0dd4f7 100644 --- a/gctrpc/rpc.proto +++ b/gctrpc/rpc.proto @@ -1119,6 +1119,33 @@ message ShutdownRequest {} message ShutdownResponse {} +message GetTechnicalAnalysisRequest { + string exchange = 1; + CurrencyPair pair = 2; + string asset_type = 3; + string algorithm_type = 4; + int64 interval = 5; + google.protobuf.Timestamp start = 6; + google.protobuf.Timestamp end = 7; + int64 period = 8; + int64 fast_period = 9; + int64 slow_period = 10; + double standard_deviation_up = 11; + double standard_deviation_down = 12; + int64 moving_average_type = 13; + string other_exchange = 14; + CurrencyPair other_pair = 15; + string other_asset_type = 16; +} + +message ListOfSignals { + repeated double signals = 1; +} + +message GetTechnicalAnalysisResponse { + map signals = 1; +} + service GoCryptoTraderService { rpc GetInfo(GetInfoRequest) returns (GetInfoResponse) { option (google.api.http) = { @@ -1722,4 +1749,9 @@ service GoCryptoTraderService { get: "/v1/shutdown" }; } + rpc GetTechnicalAnalysis(GetTechnicalAnalysisRequest) returns (GetTechnicalAnalysisResponse) { + option (google.api.http) = { + get: "/v1/gettechnicalanalysis" + }; + } } diff --git a/gctrpc/rpc.swagger.json b/gctrpc/rpc.swagger.json index 30773b56..88fd4ab5 100644 --- a/gctrpc/rpc.swagger.json +++ b/gctrpc/rpc.swagger.json @@ -2577,6 +2577,159 @@ ] } }, + "/v1/gettechnicalanalysis": { + "get": { + "operationId": "GoCryptoTraderService_GetTechnicalAnalysis", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/gctrpcGetTechnicalAnalysisResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "exchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.delimiter", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.base", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "pair.quote", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "assetType", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "algorithmType", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "interval", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "start", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "end", + "in": "query", + "required": false, + "type": "string", + "format": "date-time" + }, + { + "name": "period", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "fastPeriod", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "slowPeriod", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "standardDeviationUp", + "in": "query", + "required": false, + "type": "number", + "format": "double" + }, + { + "name": "standardDeviationDown", + "in": "query", + "required": false, + "type": "number", + "format": "double" + }, + { + "name": "movingAverageType", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "otherExchange", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "otherPair.delimiter", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "otherPair.base", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "otherPair.quote", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "otherAssetType", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "GoCryptoTraderService" + ] + } + }, "/v1/getticker": { "post": { "operationId": "GoCryptoTraderService_GetTicker", @@ -4989,6 +5142,17 @@ } } }, + "gctrpcGetTechnicalAnalysisResponse": { + "type": "object", + "properties": { + "signals": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/gctrpcListOfSignals" + } + } + } + }, "gctrpcGetTickerRequest": { "type": "object", "properties": { @@ -5014,6 +5178,18 @@ } } }, + "gctrpcListOfSignals": { + "type": "object", + "properties": { + "signals": { + "type": "array", + "items": { + "type": "number", + "format": "double" + } + } + } + }, "gctrpcModifyOrderResponse": { "type": "object", "properties": { diff --git a/gctrpc/rpc_grpc.pb.go b/gctrpc/rpc_grpc.pb.go index 43308448..b77ed02f 100644 --- a/gctrpc/rpc_grpc.pb.go +++ b/gctrpc/rpc_grpc.pb.go @@ -119,6 +119,7 @@ type GoCryptoTraderServiceClient interface { GetFuturesPositions(ctx context.Context, in *GetFuturesPositionsRequest, opts ...grpc.CallOption) (*GetFuturesPositionsResponse, error) GetCollateral(ctx context.Context, in *GetCollateralRequest, opts ...grpc.CallOption) (*GetCollateralResponse, error) Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*ShutdownResponse, error) + GetTechnicalAnalysis(ctx context.Context, in *GetTechnicalAnalysisRequest, opts ...grpc.CallOption) (*GetTechnicalAnalysisResponse, error) } type goCryptoTraderServiceClient struct { @@ -1140,6 +1141,15 @@ func (c *goCryptoTraderServiceClient) Shutdown(ctx context.Context, in *Shutdown return out, nil } +func (c *goCryptoTraderServiceClient) GetTechnicalAnalysis(ctx context.Context, in *GetTechnicalAnalysisRequest, opts ...grpc.CallOption) (*GetTechnicalAnalysisResponse, error) { + out := new(GetTechnicalAnalysisResponse) + err := c.cc.Invoke(ctx, "/gctrpc.GoCryptoTraderService/GetTechnicalAnalysis", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // GoCryptoTraderServiceServer is the server API for GoCryptoTraderService service. // All implementations must embed UnimplementedGoCryptoTraderServiceServer // for forward compatibility @@ -1241,6 +1251,7 @@ type GoCryptoTraderServiceServer interface { GetFuturesPositions(context.Context, *GetFuturesPositionsRequest) (*GetFuturesPositionsResponse, error) GetCollateral(context.Context, *GetCollateralRequest) (*GetCollateralResponse, error) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) + GetTechnicalAnalysis(context.Context, *GetTechnicalAnalysisRequest) (*GetTechnicalAnalysisResponse, error) mustEmbedUnimplementedGoCryptoTraderServiceServer() } @@ -1539,6 +1550,9 @@ func (UnimplementedGoCryptoTraderServiceServer) GetCollateral(context.Context, * func (UnimplementedGoCryptoTraderServiceServer) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Shutdown not implemented") } +func (UnimplementedGoCryptoTraderServiceServer) GetTechnicalAnalysis(context.Context, *GetTechnicalAnalysisRequest) (*GetTechnicalAnalysisResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTechnicalAnalysis not implemented") +} func (UnimplementedGoCryptoTraderServiceServer) mustEmbedUnimplementedGoCryptoTraderServiceServer() {} // UnsafeGoCryptoTraderServiceServer may be embedded to opt out of forward compatibility for this service. @@ -3316,6 +3330,24 @@ func _GoCryptoTraderService_Shutdown_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _GoCryptoTraderService_GetTechnicalAnalysis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTechnicalAnalysisRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GoCryptoTraderServiceServer).GetTechnicalAnalysis(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gctrpc.GoCryptoTraderService/GetTechnicalAnalysis", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GoCryptoTraderServiceServer).GetTechnicalAnalysis(ctx, req.(*GetTechnicalAnalysisRequest)) + } + return interceptor(ctx, in, info, handler) +} + // GoCryptoTraderService_ServiceDesc is the grpc.ServiceDesc for GoCryptoTraderService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -3687,6 +3719,10 @@ var GoCryptoTraderService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Shutdown", Handler: _GoCryptoTraderService_Shutdown_Handler, }, + { + MethodName: "GetTechnicalAnalysis", + Handler: _GoCryptoTraderService_GetTechnicalAnalysis_Handler, + }, }, Streams: []grpc.StreamDesc{ {