From 22324784154907b71b28f4130567178ddc522b01 Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 30 Sep 2022 14:15:10 +1000 Subject: [PATCH] backtester: run manager (#1040) * begins defining run management options * fleshes out concept * completes fund manager and RPC commands * coverage and improvements * adds coverage, and bad log concept * simplifies output at expense of races * removes run logging for now. tightens races. adds cov * Lints thine splints * Fixes stopping and clearing bugs * some niteroos * fix races --- backtester/btcli/commands.go | 296 +++- backtester/btcli/main.go | 7 + backtester/btrpc/btrpc.pb.go | 1304 +++++++++++++++-- backtester/btrpc/btrpc.pb.gw.go | 581 +++++++- backtester/btrpc/btrpc.proto | 108 +- backtester/btrpc/btrpc.swagger.json | 316 +++- backtester/btrpc/btrpc_grpc.pb.go | 252 ++++ backtester/engine/backtest.go | 181 ++- backtester/engine/backtest_test.go | 247 +++- backtester/engine/backtest_types.go | 30 + backtester/engine/grpcserver.go | 300 +++- backtester/engine/grpcserver_test.go | 337 ++++- backtester/engine/runmanager.go | 214 +++ backtester/engine/runmanager_test.go | 425 ++++++ backtester/engine/setup.go | 57 +- backtester/engine/setup_test.go | 52 +- .../eventhandlers/statistics/statistics.go | 9 + .../statistics/statistics_types.go | 95 +- backtester/main.go | 26 +- backtester/report/report.go | 3 + common/timeperiods/timeperiods.go | 2 +- exchanges/binance/binance.go | 2 +- exchanges/binanceus/binanceus.go | 6 +- exchanges/kline/kline.go | 2 +- log/logger_multiwriter.go | 2 +- 25 files changed, 4578 insertions(+), 276 deletions(-) create mode 100644 backtester/engine/runmanager.go create mode 100644 backtester/engine/runmanager_test.go diff --git a/backtester/btcli/commands.go b/backtester/btcli/commands.go index d4bd1f34dc4..0a50e9d44cd 100644 --- a/backtester/btcli/commands.go +++ b/backtester/btcli/commands.go @@ -10,6 +10,19 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) +var ( + doNotRunFlag = &cli.BoolFlag{ + Name: "donotrunimmediately", + Aliases: []string{"dnr"}, + Usage: "if true, will load the strategy, but will not execute until another command is sent", + } + doNotStoreFlag = &cli.BoolFlag{ + Name: "donotstore", + Aliases: []string{"dns"}, + Usage: "if true, will not store the run internally - cannot be run in conjunction with dnr", + } +) + var executeStrategyFromFileCommand = &cli.Command{ Name: "executestrategyfromfile", Usage: "runs the strategy from a config file", @@ -21,6 +34,8 @@ var executeStrategyFromFileCommand = &cli.Command{ Aliases: []string{"p"}, Usage: "the filepath to a strategy to execute", }, + doNotRunFlag, + doNotStoreFlag, }, } @@ -32,7 +47,7 @@ func executeStrategyFromFile(c *cli.Context) error { defer closeConn(conn, cancel) if c.NArg() == 0 && c.NumFlags() == 0 { - return cli.ShowCommandHelp(c, "executestrategyfromfile") + return cli.ShowCommandHelp(c, c.Command.Name) } var path string @@ -42,11 +57,22 @@ func executeStrategyFromFile(c *cli.Context) error { path = c.Args().First() } + var dnr bool + if c.IsSet("donotrunimmediately") { + dnr = c.Bool("donotrunimmediately") + } + var dns bool + if c.IsSet("donotstore") { + dns = c.Bool("donotstore") + } + client := btrpc.NewBacktesterServiceClient(conn) result, err := client.ExecuteStrategyFromFile( c.Context, &btrpc.ExecuteStrategyFromFileRequest{ - StrategyFilePath: path, + StrategyFilePath: path, + DoNotRunImmediately: dnr, + DoNotStore: dns, }, ) @@ -58,11 +84,264 @@ func executeStrategyFromFile(c *cli.Context) error { return nil } +var listAllRunsCommand = &cli.Command{ + Name: "listallruns", + Usage: "returns a list of all loaded backtest/livestrategy runs", + Action: listAllRuns, +} + +func listAllRuns(c *cli.Context) error { + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + client := btrpc.NewBacktesterServiceClient(conn) + result, err := client.ListAllRuns( + c.Context, + &btrpc.ListAllRunsRequest{}, + ) + + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +var startRunCommand = &cli.Command{ + Name: "startrun", + Usage: "executes a strategy loaded into the server", + ArgsUsage: "", + Action: startRun, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "id", + Usage: "the id of the backtest/livestrategy run", + }, + }, +} + +func startRun(c *cli.Context) error { + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowCommandHelp(c, c.Command.Name) + } + + var id string + if c.IsSet("id") { + id = c.String("id") + } else { + id = c.Args().First() + } + + client := btrpc.NewBacktesterServiceClient(conn) + result, err := client.StartRun( + c.Context, + &btrpc.StartRunRequest{ + Id: id, + }, + ) + + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +var startAllRunsCommand = &cli.Command{ + Name: "startallruns", + Usage: "executes all strategies loaded into the server that have not been run", + Action: startAllRuns, +} + +func startAllRuns(c *cli.Context) error { + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + client := btrpc.NewBacktesterServiceClient(conn) + result, err := client.StartAllRuns( + c.Context, + &btrpc.StartAllRunsRequest{}, + ) + + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +var stopRunCommand = &cli.Command{ + Name: "stoprun", + Usage: "stops a strategy loaded into the server", + ArgsUsage: "", + Action: stopRun, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "id", + Usage: "the id of the backtest/livestrategy run", + }, + }, +} + +func stopRun(c *cli.Context) error { + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowCommandHelp(c, c.Command.Name) + } + + var id string + if c.IsSet("id") { + id = c.String("id") + } else { + id = c.Args().First() + } + + client := btrpc.NewBacktesterServiceClient(conn) + result, err := client.StopRun( + c.Context, + &btrpc.StopRunRequest{ + Id: id, + }, + ) + + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +var stopAllRunsCommand = &cli.Command{ + Name: "stopallruns", + Usage: "stops all strategies loaded into the server", + Action: stopAllRuns, +} + +func stopAllRuns(c *cli.Context) error { + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + client := btrpc.NewBacktesterServiceClient(conn) + result, err := client.StopAllRuns( + c.Context, + &btrpc.StopAllRunsRequest{}, + ) + + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +var clearRunCommand = &cli.Command{ + Name: "clearrun", + Usage: "clears/deletes a strategy loaded into the server - if it is not running", + ArgsUsage: "", + Action: clearRun, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "id", + Usage: "the id of the backtest/livestrategy run", + }, + }, +} + +func clearRun(c *cli.Context) error { + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + if c.NArg() == 0 && c.NumFlags() == 0 { + return cli.ShowCommandHelp(c, c.Command.Name) + } + + var id string + if c.IsSet("id") { + id = c.String("id") + } else { + id = c.Args().First() + } + + client := btrpc.NewBacktesterServiceClient(conn) + result, err := client.ClearRun( + c.Context, + &btrpc.ClearRunRequest{ + Id: id, + }, + ) + + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + +var clearAllRunsCommand = &cli.Command{ + Name: "clearallruns", + Usage: "clears all strategies loaded into the server. Only runs not actively running will be cleared", + Action: clearAllRuns, +} + +func clearAllRuns(c *cli.Context) error { + conn, cancel, err := setupClient(c) + if err != nil { + return err + } + defer closeConn(conn, cancel) + + client := btrpc.NewBacktesterServiceClient(conn) + result, err := client.ClearAllRuns( + c.Context, + &btrpc.ClearAllRunsRequest{}, + ) + + if err != nil { + return err + } + + jsonOutput(result) + return nil +} + var executeStrategyFromConfigCommand = &cli.Command{ Name: "executestrategyfromconfig", Usage: "runs the default strategy config but via passing in as a struct instead of a filepath - this is a proof-of-concept implementation", Description: "the cli is not a good place to manage this type of command with n variables to pass in from a command line", Action: executeStrategyFromConfig, + Flags: []cli.Flag{ + doNotRunFlag, + doNotStoreFlag, + }, } // executeStrategyFromConfig this is a proof of concept command @@ -235,11 +514,22 @@ func executeStrategyFromConfig(c *cli.Context) error { }, } + var dnr bool + if c.IsSet("donotrunimmediately") { + dnr = c.Bool("donotrunimmediately") + } + var dns bool + if c.IsSet("donotstore") { + dns = c.Bool("donotstore") + } + client := btrpc.NewBacktesterServiceClient(conn) result, err := client.ExecuteStrategyFromConfig( c.Context, &btrpc.ExecuteStrategyFromConfigRequest{ - Config: cfg, + Config: cfg, + DoNotRunImmediately: dnr, + DoNotStore: dns, }, ) diff --git a/backtester/btcli/main.go b/backtester/btcli/main.go index b57dbe0498d..40e4f180811 100644 --- a/backtester/btcli/main.go +++ b/backtester/btcli/main.go @@ -112,6 +112,13 @@ func main() { app.Commands = []*cli.Command{ executeStrategyFromFileCommand, executeStrategyFromConfigCommand, + listAllRunsCommand, + startRunCommand, + startAllRunsCommand, + stopRunCommand, + stopAllRunsCommand, + clearRunCommand, + clearAllRunsCommand, } ctx, cancel := context.WithCancel(context.Background()) diff --git a/backtester/btrpc/btrpc.pb.go b/backtester/btrpc/btrpc.pb.go index 13bb2a485a8..5e211aa25e2 100644 --- a/backtester/btrpc/btrpc.pb.go +++ b/backtester/btrpc/btrpc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 +// protoc-gen-go v1.28.1 // protoc (unknown) // source: btrpc.proto @@ -1657,32 +1657,704 @@ func (x *Config) GetStatisticSettings() *StatisticSettings { return nil } +type RunSummary struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + StrategyName string `protobuf:"bytes,2,opt,name=strategy_name,json=strategyName,proto3" json:"strategy_name,omitempty"` + DateLoaded string `protobuf:"bytes,3,opt,name=date_loaded,json=dateLoaded,proto3" json:"date_loaded,omitempty"` + DateStarted string `protobuf:"bytes,4,opt,name=date_started,json=dateStarted,proto3" json:"date_started,omitempty"` + DateEnded string `protobuf:"bytes,5,opt,name=date_ended,json=dateEnded,proto3" json:"date_ended,omitempty"` + Closed bool `protobuf:"varint,6,opt,name=closed,proto3" json:"closed,omitempty"` + LiveTesting bool `protobuf:"varint,7,opt,name=live_testing,json=liveTesting,proto3" json:"live_testing,omitempty"` + RealOrders bool `protobuf:"varint,8,opt,name=real_orders,json=realOrders,proto3" json:"real_orders,omitempty"` +} + +func (x *RunSummary) Reset() { + *x = RunSummary{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunSummary) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunSummary) ProtoMessage() {} + +func (x *RunSummary) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunSummary.ProtoReflect.Descriptor instead. +func (*RunSummary) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{22} +} + +func (x *RunSummary) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *RunSummary) GetStrategyName() string { + if x != nil { + return x.StrategyName + } + return "" +} + +func (x *RunSummary) GetDateLoaded() string { + if x != nil { + return x.DateLoaded + } + return "" +} + +func (x *RunSummary) GetDateStarted() string { + if x != nil { + return x.DateStarted + } + return "" +} + +func (x *RunSummary) GetDateEnded() string { + if x != nil { + return x.DateEnded + } + return "" +} + +func (x *RunSummary) GetClosed() bool { + if x != nil { + return x.Closed + } + return false +} + +func (x *RunSummary) GetLiveTesting() bool { + if x != nil { + return x.LiveTesting + } + return false +} + +func (x *RunSummary) GetRealOrders() bool { + if x != nil { + return x.RealOrders + } + return false +} + // Requests and responses type ExecuteStrategyFromFileRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - StrategyFilePath string `protobuf:"bytes,1,opt,name=strategy_file_path,json=strategyFilePath,proto3" json:"strategy_file_path,omitempty"` + StrategyFilePath string `protobuf:"bytes,1,opt,name=strategy_file_path,json=strategyFilePath,proto3" json:"strategy_file_path,omitempty"` + DoNotRunImmediately bool `protobuf:"varint,2,opt,name=do_not_run_immediately,json=doNotRunImmediately,proto3" json:"do_not_run_immediately,omitempty"` + DoNotStore bool `protobuf:"varint,3,opt,name=do_not_store,json=doNotStore,proto3" json:"do_not_store,omitempty"` +} + +func (x *ExecuteStrategyFromFileRequest) Reset() { + *x = ExecuteStrategyFromFileRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecuteStrategyFromFileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteStrategyFromFileRequest) ProtoMessage() {} + +func (x *ExecuteStrategyFromFileRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteStrategyFromFileRequest.ProtoReflect.Descriptor instead. +func (*ExecuteStrategyFromFileRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{23} +} + +func (x *ExecuteStrategyFromFileRequest) GetStrategyFilePath() string { + if x != nil { + return x.StrategyFilePath + } + return "" +} + +func (x *ExecuteStrategyFromFileRequest) GetDoNotRunImmediately() bool { + if x != nil { + return x.DoNotRunImmediately + } + return false +} + +func (x *ExecuteStrategyFromFileRequest) GetDoNotStore() bool { + if x != nil { + return x.DoNotStore + } + return false +} + +type ExecuteStrategyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Run *RunSummary `protobuf:"bytes,1,opt,name=run,proto3" json:"run,omitempty"` +} + +func (x *ExecuteStrategyResponse) Reset() { + *x = ExecuteStrategyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecuteStrategyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteStrategyResponse) ProtoMessage() {} + +func (x *ExecuteStrategyResponse) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteStrategyResponse.ProtoReflect.Descriptor instead. +func (*ExecuteStrategyResponse) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{24} +} + +func (x *ExecuteStrategyResponse) GetRun() *RunSummary { + if x != nil { + return x.Run + } + return nil +} + +type ExecuteStrategyFromConfigRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DoNotRunImmediately bool `protobuf:"varint,1,opt,name=do_not_run_immediately,json=doNotRunImmediately,proto3" json:"do_not_run_immediately,omitempty"` + DoNotStore bool `protobuf:"varint,2,opt,name=do_not_store,json=doNotStore,proto3" json:"do_not_store,omitempty"` + Config *Config `protobuf:"bytes,3,opt,name=config,proto3" json:"config,omitempty"` +} + +func (x *ExecuteStrategyFromConfigRequest) Reset() { + *x = ExecuteStrategyFromConfigRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExecuteStrategyFromConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExecuteStrategyFromConfigRequest) ProtoMessage() {} + +func (x *ExecuteStrategyFromConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExecuteStrategyFromConfigRequest.ProtoReflect.Descriptor instead. +func (*ExecuteStrategyFromConfigRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{25} +} + +func (x *ExecuteStrategyFromConfigRequest) GetDoNotRunImmediately() bool { + if x != nil { + return x.DoNotRunImmediately + } + return false +} + +func (x *ExecuteStrategyFromConfigRequest) GetDoNotStore() bool { + if x != nil { + return x.DoNotStore + } + return false +} + +func (x *ExecuteStrategyFromConfigRequest) GetConfig() *Config { + if x != nil { + return x.Config + } + return nil +} + +type ListAllRunsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ListAllRunsRequest) Reset() { + *x = ListAllRunsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListAllRunsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAllRunsRequest) ProtoMessage() {} + +func (x *ListAllRunsRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListAllRunsRequest.ProtoReflect.Descriptor instead. +func (*ListAllRunsRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{26} +} + +type ListAllRunsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Runs []*RunSummary `protobuf:"bytes,1,rep,name=runs,proto3" json:"runs,omitempty"` +} + +func (x *ListAllRunsResponse) Reset() { + *x = ListAllRunsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListAllRunsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListAllRunsResponse) ProtoMessage() {} + +func (x *ListAllRunsResponse) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListAllRunsResponse.ProtoReflect.Descriptor instead. +func (*ListAllRunsResponse) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{27} +} + +func (x *ListAllRunsResponse) GetRuns() []*RunSummary { + if x != nil { + return x.Runs + } + return nil +} + +type StopRunRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *StopRunRequest) Reset() { + *x = StopRunRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopRunRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopRunRequest) ProtoMessage() {} + +func (x *StopRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopRunRequest.ProtoReflect.Descriptor instead. +func (*StopRunRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{28} +} + +func (x *StopRunRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type StopRunResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StoppedRun *RunSummary `protobuf:"bytes,1,opt,name=stopped_run,json=stoppedRun,proto3" json:"stopped_run,omitempty"` +} + +func (x *StopRunResponse) Reset() { + *x = StopRunResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopRunResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopRunResponse) ProtoMessage() {} + +func (x *StopRunResponse) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopRunResponse.ProtoReflect.Descriptor instead. +func (*StopRunResponse) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{29} +} + +func (x *StopRunResponse) GetStoppedRun() *RunSummary { + if x != nil { + return x.StoppedRun + } + return nil +} + +type StartRunRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *StartRunRequest) Reset() { + *x = StartRunRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRunRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRunRequest) ProtoMessage() {} + +func (x *StartRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRunRequest.ProtoReflect.Descriptor instead. +func (*StartRunRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{30} +} + +func (x *StartRunRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type StartRunResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Started bool `protobuf:"varint,1,opt,name=started,proto3" json:"started,omitempty"` +} + +func (x *StartRunResponse) Reset() { + *x = StartRunResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRunResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRunResponse) ProtoMessage() {} + +func (x *StartRunResponse) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRunResponse.ProtoReflect.Descriptor instead. +func (*StartRunResponse) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{31} +} + +func (x *StartRunResponse) GetStarted() bool { + if x != nil { + return x.Started + } + return false +} + +type StartAllRunsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StartAllRunsRequest) Reset() { + *x = StartAllRunsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartAllRunsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartAllRunsRequest) ProtoMessage() {} + +func (x *StartAllRunsRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[32] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartAllRunsRequest.ProtoReflect.Descriptor instead. +func (*StartAllRunsRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{32} +} + +type StartAllRunsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunsStarted []string `protobuf:"bytes,1,rep,name=runs_started,json=runsStarted,proto3" json:"runs_started,omitempty"` +} + +func (x *StartAllRunsResponse) Reset() { + *x = StartAllRunsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartAllRunsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartAllRunsResponse) ProtoMessage() {} + +func (x *StartAllRunsResponse) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[33] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartAllRunsResponse.ProtoReflect.Descriptor instead. +func (*StartAllRunsResponse) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{33} +} + +func (x *StartAllRunsResponse) GetRunsStarted() []string { + if x != nil { + return x.RunsStarted + } + return nil +} + +type StopAllRunsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StopAllRunsRequest) Reset() { + *x = StopAllRunsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[34] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopAllRunsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopAllRunsRequest) ProtoMessage() {} + +func (x *StopAllRunsRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[34] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StopAllRunsRequest.ProtoReflect.Descriptor instead. +func (*StopAllRunsRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{34} +} + +type StopAllRunsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunsStopped []*RunSummary `protobuf:"bytes,1,rep,name=runs_stopped,json=runsStopped,proto3" json:"runs_stopped,omitempty"` } -func (x *ExecuteStrategyFromFileRequest) Reset() { - *x = ExecuteStrategyFromFileRequest{} +func (x *StopAllRunsResponse) Reset() { + *x = StopAllRunsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_btrpc_proto_msgTypes[22] + mi := &file_btrpc_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ExecuteStrategyFromFileRequest) String() string { +func (x *StopAllRunsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExecuteStrategyFromFileRequest) ProtoMessage() {} +func (*StopAllRunsResponse) ProtoMessage() {} -func (x *ExecuteStrategyFromFileRequest) ProtoReflect() protoreflect.Message { - mi := &file_btrpc_proto_msgTypes[22] +func (x *StopAllRunsResponse) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1693,44 +2365,43 @@ func (x *ExecuteStrategyFromFileRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExecuteStrategyFromFileRequest.ProtoReflect.Descriptor instead. -func (*ExecuteStrategyFromFileRequest) Descriptor() ([]byte, []int) { - return file_btrpc_proto_rawDescGZIP(), []int{22} +// Deprecated: Use StopAllRunsResponse.ProtoReflect.Descriptor instead. +func (*StopAllRunsResponse) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{35} } -func (x *ExecuteStrategyFromFileRequest) GetStrategyFilePath() string { +func (x *StopAllRunsResponse) GetRunsStopped() []*RunSummary { if x != nil { - return x.StrategyFilePath + return x.RunsStopped } - return "" + return nil } -type ExecuteStrategyResponse struct { +type ClearRunRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` } -func (x *ExecuteStrategyResponse) Reset() { - *x = ExecuteStrategyResponse{} +func (x *ClearRunRequest) Reset() { + *x = ClearRunRequest{} if protoimpl.UnsafeEnabled { - mi := &file_btrpc_proto_msgTypes[23] + mi := &file_btrpc_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ExecuteStrategyResponse) String() string { +func (x *ClearRunRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExecuteStrategyResponse) ProtoMessage() {} +func (*ClearRunRequest) ProtoMessage() {} -func (x *ExecuteStrategyResponse) ProtoReflect() protoreflect.Message { - mi := &file_btrpc_proto_msgTypes[23] +func (x *ClearRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1741,50 +2412,129 @@ func (x *ExecuteStrategyResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExecuteStrategyResponse.ProtoReflect.Descriptor instead. -func (*ExecuteStrategyResponse) Descriptor() ([]byte, []int) { - return file_btrpc_proto_rawDescGZIP(), []int{23} +// Deprecated: Use ClearRunRequest.ProtoReflect.Descriptor instead. +func (*ClearRunRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{36} } -func (x *ExecuteStrategyResponse) GetSuccess() bool { +func (x *ClearRunRequest) GetId() string { if x != nil { - return x.Success + return x.Id } - return false + return "" +} + +type ClearRunResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClearedRun *RunSummary `protobuf:"bytes,1,opt,name=cleared_run,json=clearedRun,proto3" json:"cleared_run,omitempty"` +} + +func (x *ClearRunResponse) Reset() { + *x = ClearRunResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[37] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClearRunResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClearRunResponse) ProtoMessage() {} + +func (x *ClearRunResponse) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[37] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClearRunResponse.ProtoReflect.Descriptor instead. +func (*ClearRunResponse) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{37} } -func (x *ExecuteStrategyResponse) GetMessage() string { +func (x *ClearRunResponse) GetClearedRun() *RunSummary { if x != nil { - return x.Message + return x.ClearedRun } - return "" + return nil } -type ExecuteStrategyFromConfigRequest struct { +type ClearAllRunsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields +} - Config *Config `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` +func (x *ClearAllRunsRequest) Reset() { + *x = ClearAllRunsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_btrpc_proto_msgTypes[38] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (x *ExecuteStrategyFromConfigRequest) Reset() { - *x = ExecuteStrategyFromConfigRequest{} +func (x *ClearAllRunsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClearAllRunsRequest) ProtoMessage() {} + +func (x *ClearAllRunsRequest) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[38] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClearAllRunsRequest.ProtoReflect.Descriptor instead. +func (*ClearAllRunsRequest) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{38} +} + +type ClearAllRunsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClearedRuns []*RunSummary `protobuf:"bytes,1,rep,name=cleared_runs,json=clearedRuns,proto3" json:"cleared_runs,omitempty"` + RemainingRuns []*RunSummary `protobuf:"bytes,2,rep,name=remaining_runs,json=remainingRuns,proto3" json:"remaining_runs,omitempty"` +} + +func (x *ClearAllRunsResponse) Reset() { + *x = ClearAllRunsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_btrpc_proto_msgTypes[24] + mi := &file_btrpc_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ExecuteStrategyFromConfigRequest) String() string { +func (x *ClearAllRunsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ExecuteStrategyFromConfigRequest) ProtoMessage() {} +func (*ClearAllRunsResponse) ProtoMessage() {} -func (x *ExecuteStrategyFromConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_btrpc_proto_msgTypes[24] +func (x *ClearAllRunsResponse) ProtoReflect() protoreflect.Message { + mi := &file_btrpc_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1795,14 +2545,21 @@ func (x *ExecuteStrategyFromConfigRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ExecuteStrategyFromConfigRequest.ProtoReflect.Descriptor instead. -func (*ExecuteStrategyFromConfigRequest) Descriptor() ([]byte, []int) { - return file_btrpc_proto_rawDescGZIP(), []int{24} +// Deprecated: Use ClearAllRunsResponse.ProtoReflect.Descriptor instead. +func (*ClearAllRunsResponse) Descriptor() ([]byte, []int) { + return file_btrpc_proto_rawDescGZIP(), []int{39} } -func (x *ExecuteStrategyFromConfigRequest) GetConfig() *Config { +func (x *ClearAllRunsResponse) GetClearedRuns() []*RunSummary { if x != nil { - return x.Config + return x.ClearedRuns + } + return nil +} + +func (x *ClearAllRunsResponse) GetRemainingRuns() []*RunSummary { + if x != nil { + return x.RemainingRuns } return nil } @@ -2097,44 +2854,155 @@ var file_btrpc_proto_rawDesc = []byte{ 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x11, 0x73, 0x74, 0x61, 0x74, 0x69, 0x73, 0x74, 0x69, 0x63, 0x53, 0x65, 0x74, - 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x4e, 0x0a, 0x1e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, - 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x46, 0x69, 0x6c, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x69, 0x6c, - 0x65, 0x50, 0x61, 0x74, 0x68, 0x22, 0x4d, 0x0a, 0x17, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, - 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x49, 0x0a, 0x20, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, - 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, - 0xa9, 0x02, 0x0a, 0x11, 0x42, 0x61, 0x63, 0x6b, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x85, 0x01, 0x0a, 0x17, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x46, 0x69, 0x6c, - 0x65, 0x12, 0x25, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, - 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, - 0x12, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x73, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x66, 0x72, 0x6f, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x8b, 0x01, - 0x0a, 0x19, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x2e, 0x62, 0x74, - 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, - 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, - 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x66, 0x72, 0x6f, 0x6d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x3a, 0x5a, 0x38, 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, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x65, 0x73, 0x74, 0x65, - 0x72, 0x2f, 0x62, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x80, 0x02, 0x0a, 0x0a, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x65, 0x67, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x61, 0x74, + 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x64, 0x61, 0x74, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x61, + 0x74, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x63, 0x6c, + 0x6f, 0x73, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6c, 0x69, 0x76, 0x65, + 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x6c, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x65, + 0x61, 0x6c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x73, 0x22, 0xa5, 0x01, 0x0a, 0x1e, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x33, 0x0a, 0x16, 0x64, 0x6f, 0x5f, + 0x6e, 0x6f, 0x74, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, + 0x65, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x6f, 0x4e, 0x6f, 0x74, + 0x52, 0x75, 0x6e, 0x49, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x12, 0x20, + 0x0a, 0x0c, 0x64, 0x6f, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x6f, 0x4e, 0x6f, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, + 0x22, 0x3e, 0x0a, 0x17, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x03, 0x72, + 0x75, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x03, 0x72, 0x75, 0x6e, + 0x22, 0xa0, 0x01, 0x0a, 0x20, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, + 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x16, 0x64, 0x6f, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, + 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x6f, 0x4e, 0x6f, 0x74, 0x52, 0x75, 0x6e, 0x49, + 0x6d, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x0c, 0x64, 0x6f, + 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0a, 0x64, 0x6f, 0x4e, 0x6f, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x25, 0x0a, 0x06, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x62, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x22, 0x14, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3c, 0x0a, 0x13, 0x4c, 0x69, 0x73, + 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x25, 0x0a, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x52, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x22, 0x20, 0x0a, 0x0e, 0x53, 0x74, 0x6f, 0x70, 0x52, + 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x45, 0x0a, 0x0f, 0x53, 0x74, 0x6f, + 0x70, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x0b, + 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0a, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x52, 0x75, 0x6e, + 0x22, 0x21, 0x0a, 0x0f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x22, 0x2c, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x75, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, + 0x64, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x39, 0x0a, 0x14, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75, 0x6e, 0x73, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x65, 0x64, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x75, + 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4b, 0x0a, 0x13, 0x53, 0x74, 0x6f, + 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x34, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, + 0x75, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0b, 0x72, 0x75, 0x6e, 0x73, 0x53, + 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x22, 0x21, 0x0a, 0x0f, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x52, + 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x46, 0x0a, 0x10, 0x43, 0x6c, 0x65, + 0x61, 0x72, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, + 0x0b, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x64, 0x52, 0x75, + 0x6e, 0x22, 0x15, 0x0a, 0x13, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x86, 0x01, 0x0a, 0x14, 0x43, 0x6c, 0x65, + 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x34, 0x0a, 0x0c, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x75, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0b, 0x63, 0x6c, 0x65, 0x61, + 0x72, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x61, 0x69, + 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, + 0x72, 0x79, 0x52, 0x0d, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6e, + 0x73, 0x32, 0xa2, 0x07, 0x0a, 0x11, 0x42, 0x61, 0x63, 0x6b, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x85, 0x01, 0x0a, 0x17, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x46, + 0x69, 0x6c, 0x65, 0x12, 0x25, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x46, + 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x62, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, + 0x67, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1d, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x66, 0x72, 0x6f, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x12, + 0x8b, 0x01, 0x0a, 0x19, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x27, 0x2e, + 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, + 0x61, 0x74, 0x65, 0x67, 0x79, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x22, 0x1d, + 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x66, 0x72, 0x6f, 0x6d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5d, 0x0a, + 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x12, 0x19, 0x2e, 0x62, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, + 0x2f, 0x6c, 0x69, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x72, 0x75, 0x6e, 0x73, 0x12, 0x51, 0x0a, 0x08, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x75, 0x6e, 0x12, 0x16, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, + 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x17, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x75, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x0e, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x72, 0x75, 0x6e, 0x12, + 0x61, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x12, + 0x1a, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x41, 0x6c, 0x6c, + 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x62, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, + 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x61, 0x6c, 0x6c, 0x72, 0x75, + 0x6e, 0x73, 0x12, 0x4d, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x75, 0x6e, 0x12, 0x15, 0x2e, + 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, + 0x70, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x0d, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x72, 0x75, + 0x6e, 0x12, 0x5d, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, + 0x12, 0x19, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, + 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x62, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x22, + 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x61, 0x6c, 0x6c, 0x72, 0x75, 0x6e, 0x73, + 0x12, 0x51, 0x0a, 0x08, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x52, 0x75, 0x6e, 0x12, 0x16, 0x2e, 0x62, + 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x65, + 0x61, 0x72, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x2a, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6c, 0x65, 0x61, 0x72, + 0x72, 0x75, 0x6e, 0x12, 0x61, 0x0a, 0x0c, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, 0x52, + 0x75, 0x6e, 0x73, 0x12, 0x1a, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x65, 0x61, + 0x72, 0x41, 0x6c, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x62, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x41, 0x6c, 0x6c, + 0x52, 0x75, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x12, 0x2a, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x61, + 0x6c, 0x6c, 0x72, 0x75, 0x6e, 0x73, 0x42, 0x3a, 0x5a, 0x38, 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, 0x62, 0x61, 0x63, 0x6b, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x62, 0x74, 0x72, + 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2149,7 +3017,7 @@ func file_btrpc_proto_rawDescGZIP() []byte { return file_btrpc_proto_rawDescData } -var file_btrpc_proto_msgTypes = make([]protoimpl.MessageInfo, 25) +var file_btrpc_proto_msgTypes = make([]protoimpl.MessageInfo, 40) var file_btrpc_proto_goTypes = []interface{}{ (*StrategySettings)(nil), // 0: btrpc.StrategySettings (*CustomSettings)(nil), // 1: btrpc.CustomSettings @@ -2173,10 +3041,25 @@ var file_btrpc_proto_goTypes = []interface{}{ (*PortfolioSettings)(nil), // 19: btrpc.PortfolioSettings (*StatisticSettings)(nil), // 20: btrpc.StatisticSettings (*Config)(nil), // 21: btrpc.Config - (*ExecuteStrategyFromFileRequest)(nil), // 22: btrpc.ExecuteStrategyFromFileRequest - (*ExecuteStrategyResponse)(nil), // 23: btrpc.ExecuteStrategyResponse - (*ExecuteStrategyFromConfigRequest)(nil), // 24: btrpc.ExecuteStrategyFromConfigRequest - (*timestamppb.Timestamp)(nil), // 25: google.protobuf.Timestamp + (*RunSummary)(nil), // 22: btrpc.RunSummary + (*ExecuteStrategyFromFileRequest)(nil), // 23: btrpc.ExecuteStrategyFromFileRequest + (*ExecuteStrategyResponse)(nil), // 24: btrpc.ExecuteStrategyResponse + (*ExecuteStrategyFromConfigRequest)(nil), // 25: btrpc.ExecuteStrategyFromConfigRequest + (*ListAllRunsRequest)(nil), // 26: btrpc.ListAllRunsRequest + (*ListAllRunsResponse)(nil), // 27: btrpc.ListAllRunsResponse + (*StopRunRequest)(nil), // 28: btrpc.StopRunRequest + (*StopRunResponse)(nil), // 29: btrpc.StopRunResponse + (*StartRunRequest)(nil), // 30: btrpc.StartRunRequest + (*StartRunResponse)(nil), // 31: btrpc.StartRunResponse + (*StartAllRunsRequest)(nil), // 32: btrpc.StartAllRunsRequest + (*StartAllRunsResponse)(nil), // 33: btrpc.StartAllRunsResponse + (*StopAllRunsRequest)(nil), // 34: btrpc.StopAllRunsRequest + (*StopAllRunsResponse)(nil), // 35: btrpc.StopAllRunsResponse + (*ClearRunRequest)(nil), // 36: btrpc.ClearRunRequest + (*ClearRunResponse)(nil), // 37: btrpc.ClearRunResponse + (*ClearAllRunsRequest)(nil), // 38: btrpc.ClearAllRunsRequest + (*ClearAllRunsResponse)(nil), // 39: btrpc.ClearAllRunsResponse + (*timestamppb.Timestamp)(nil), // 40: google.protobuf.Timestamp } var file_btrpc_proto_depIdxs = []int32{ 1, // 0: btrpc.StrategySettings.custom_settings:type_name -> btrpc.CustomSettings @@ -2186,14 +3069,14 @@ var file_btrpc_proto_depIdxs = []int32{ 4, // 4: btrpc.CurrencySettings.sell_side:type_name -> btrpc.PurchaseSide 5, // 5: btrpc.CurrencySettings.spot_details:type_name -> btrpc.SpotDetails 6, // 6: btrpc.CurrencySettings.futures_details:type_name -> btrpc.FuturesDetails - 25, // 7: btrpc.ApiData.start_date:type_name -> google.protobuf.Timestamp - 25, // 8: btrpc.ApiData.end_date:type_name -> google.protobuf.Timestamp - 25, // 9: btrpc.DbData.start_date:type_name -> google.protobuf.Timestamp - 25, // 10: btrpc.DbData.end_date:type_name -> google.protobuf.Timestamp + 40, // 7: btrpc.ApiData.start_date:type_name -> google.protobuf.Timestamp + 40, // 8: btrpc.ApiData.end_date:type_name -> google.protobuf.Timestamp + 40, // 9: btrpc.DbData.start_date:type_name -> google.protobuf.Timestamp + 40, // 10: btrpc.DbData.end_date:type_name -> google.protobuf.Timestamp 9, // 11: btrpc.DbData.config:type_name -> btrpc.DbConfig 12, // 12: btrpc.DatabaseConfig.config:type_name -> btrpc.DatabaseConnectionDetails - 25, // 13: btrpc.DatabaseData.start_date:type_name -> google.protobuf.Timestamp - 25, // 14: btrpc.DatabaseData.end_date:type_name -> google.protobuf.Timestamp + 40, // 13: btrpc.DatabaseData.start_date:type_name -> google.protobuf.Timestamp + 40, // 14: btrpc.DatabaseData.end_date:type_name -> google.protobuf.Timestamp 13, // 15: btrpc.DatabaseData.config:type_name -> btrpc.DatabaseConfig 8, // 16: btrpc.DataSettings.api_data:type_name -> btrpc.ApiData 14, // 17: btrpc.DataSettings.database_data:type_name -> btrpc.DatabaseData @@ -2208,16 +3091,37 @@ var file_btrpc_proto_depIdxs = []int32{ 17, // 26: btrpc.Config.data_settings:type_name -> btrpc.DataSettings 19, // 27: btrpc.Config.portfolio_settings:type_name -> btrpc.PortfolioSettings 20, // 28: btrpc.Config.statistic_settings:type_name -> btrpc.StatisticSettings - 21, // 29: btrpc.ExecuteStrategyFromConfigRequest.config:type_name -> btrpc.Config - 22, // 30: btrpc.BacktesterService.ExecuteStrategyFromFile:input_type -> btrpc.ExecuteStrategyFromFileRequest - 24, // 31: btrpc.BacktesterService.ExecuteStrategyFromConfig:input_type -> btrpc.ExecuteStrategyFromConfigRequest - 23, // 32: btrpc.BacktesterService.ExecuteStrategyFromFile:output_type -> btrpc.ExecuteStrategyResponse - 23, // 33: btrpc.BacktesterService.ExecuteStrategyFromConfig:output_type -> btrpc.ExecuteStrategyResponse - 32, // [32:34] is the sub-list for method output_type - 30, // [30:32] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 22, // 29: btrpc.ExecuteStrategyResponse.run:type_name -> btrpc.RunSummary + 21, // 30: btrpc.ExecuteStrategyFromConfigRequest.config:type_name -> btrpc.Config + 22, // 31: btrpc.ListAllRunsResponse.runs:type_name -> btrpc.RunSummary + 22, // 32: btrpc.StopRunResponse.stopped_run:type_name -> btrpc.RunSummary + 22, // 33: btrpc.StopAllRunsResponse.runs_stopped:type_name -> btrpc.RunSummary + 22, // 34: btrpc.ClearRunResponse.cleared_run:type_name -> btrpc.RunSummary + 22, // 35: btrpc.ClearAllRunsResponse.cleared_runs:type_name -> btrpc.RunSummary + 22, // 36: btrpc.ClearAllRunsResponse.remaining_runs:type_name -> btrpc.RunSummary + 23, // 37: btrpc.BacktesterService.ExecuteStrategyFromFile:input_type -> btrpc.ExecuteStrategyFromFileRequest + 25, // 38: btrpc.BacktesterService.ExecuteStrategyFromConfig:input_type -> btrpc.ExecuteStrategyFromConfigRequest + 26, // 39: btrpc.BacktesterService.ListAllRuns:input_type -> btrpc.ListAllRunsRequest + 30, // 40: btrpc.BacktesterService.StartRun:input_type -> btrpc.StartRunRequest + 32, // 41: btrpc.BacktesterService.StartAllRuns:input_type -> btrpc.StartAllRunsRequest + 28, // 42: btrpc.BacktesterService.StopRun:input_type -> btrpc.StopRunRequest + 34, // 43: btrpc.BacktesterService.StopAllRuns:input_type -> btrpc.StopAllRunsRequest + 36, // 44: btrpc.BacktesterService.ClearRun:input_type -> btrpc.ClearRunRequest + 38, // 45: btrpc.BacktesterService.ClearAllRuns:input_type -> btrpc.ClearAllRunsRequest + 24, // 46: btrpc.BacktesterService.ExecuteStrategyFromFile:output_type -> btrpc.ExecuteStrategyResponse + 24, // 47: btrpc.BacktesterService.ExecuteStrategyFromConfig:output_type -> btrpc.ExecuteStrategyResponse + 27, // 48: btrpc.BacktesterService.ListAllRuns:output_type -> btrpc.ListAllRunsResponse + 31, // 49: btrpc.BacktesterService.StartRun:output_type -> btrpc.StartRunResponse + 33, // 50: btrpc.BacktesterService.StartAllRuns:output_type -> btrpc.StartAllRunsResponse + 29, // 51: btrpc.BacktesterService.StopRun:output_type -> btrpc.StopRunResponse + 35, // 52: btrpc.BacktesterService.StopAllRuns:output_type -> btrpc.StopAllRunsResponse + 37, // 53: btrpc.BacktesterService.ClearRun:output_type -> btrpc.ClearRunResponse + 39, // 54: btrpc.BacktesterService.ClearAllRuns:output_type -> btrpc.ClearAllRunsResponse + 46, // [46:55] is the sub-list for method output_type + 37, // [37:46] is the sub-list for method input_type + 37, // [37:37] is the sub-list for extension type_name + 37, // [37:37] is the sub-list for extension extendee + 0, // [0:37] is the sub-list for field type_name } func init() { file_btrpc_proto_init() } @@ -2491,7 +3395,7 @@ func file_btrpc_proto_init() { } } file_btrpc_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExecuteStrategyFromFileRequest); i { + switch v := v.(*RunSummary); i { case 0: return &v.state case 1: @@ -2503,7 +3407,7 @@ func file_btrpc_proto_init() { } } file_btrpc_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ExecuteStrategyResponse); i { + switch v := v.(*ExecuteStrategyFromFileRequest); i { case 0: return &v.state case 1: @@ -2515,6 +3419,18 @@ func file_btrpc_proto_init() { } } file_btrpc_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExecuteStrategyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExecuteStrategyFromConfigRequest); i { case 0: return &v.state @@ -2526,6 +3442,174 @@ func file_btrpc_proto_init() { return nil } } + file_btrpc_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListAllRunsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListAllRunsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopRunRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopRunResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRunRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRunResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartAllRunsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartAllRunsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopAllRunsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopAllRunsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClearRunRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClearRunResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClearAllRunsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_btrpc_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClearAllRunsResponse); 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{ @@ -2533,7 +3617,7 @@ func file_btrpc_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_btrpc_proto_rawDesc, NumEnums: 0, - NumMessages: 25, + NumMessages: 40, NumExtensions: 0, NumServices: 1, }, diff --git a/backtester/btrpc/btrpc.pb.gw.go b/backtester/btrpc/btrpc.pb.gw.go index ceb07a1e089..af889b5beb2 100644 --- a/backtester/btrpc/btrpc.pb.gw.go +++ b/backtester/btrpc/btrpc.pb.gw.go @@ -103,57 +103,414 @@ func local_request_BacktesterService_ExecuteStrategyFromConfig_0(ctx context.Con } +func request_BacktesterService_ListAllRuns_0(ctx context.Context, marshaler runtime.Marshaler, client BacktesterServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListAllRunsRequest + var metadata runtime.ServerMetadata + + msg, err := client.ListAllRuns(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_BacktesterService_ListAllRuns_0(ctx context.Context, marshaler runtime.Marshaler, server BacktesterServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListAllRunsRequest + var metadata runtime.ServerMetadata + + msg, err := server.ListAllRuns(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_BacktesterService_StartRun_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_BacktesterService_StartRun_0(ctx context.Context, marshaler runtime.Marshaler, client BacktesterServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StartRunRequest + 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_BacktesterService_StartRun_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.StartRun(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_BacktesterService_StartRun_0(ctx context.Context, marshaler runtime.Marshaler, server BacktesterServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StartRunRequest + 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_BacktesterService_StartRun_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.StartRun(ctx, &protoReq) + return msg, metadata, err + +} + +func request_BacktesterService_StartAllRuns_0(ctx context.Context, marshaler runtime.Marshaler, client BacktesterServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StartAllRunsRequest + var metadata runtime.ServerMetadata + + msg, err := client.StartAllRuns(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_BacktesterService_StartAllRuns_0(ctx context.Context, marshaler runtime.Marshaler, server BacktesterServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StartAllRunsRequest + var metadata runtime.ServerMetadata + + msg, err := server.StartAllRuns(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_BacktesterService_StopRun_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_BacktesterService_StopRun_0(ctx context.Context, marshaler runtime.Marshaler, client BacktesterServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StopRunRequest + 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_BacktesterService_StopRun_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.StopRun(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_BacktesterService_StopRun_0(ctx context.Context, marshaler runtime.Marshaler, server BacktesterServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StopRunRequest + 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_BacktesterService_StopRun_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.StopRun(ctx, &protoReq) + return msg, metadata, err + +} + +func request_BacktesterService_StopAllRuns_0(ctx context.Context, marshaler runtime.Marshaler, client BacktesterServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StopAllRunsRequest + var metadata runtime.ServerMetadata + + msg, err := client.StopAllRuns(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_BacktesterService_StopAllRuns_0(ctx context.Context, marshaler runtime.Marshaler, server BacktesterServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq StopAllRunsRequest + var metadata runtime.ServerMetadata + + msg, err := server.StopAllRuns(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_BacktesterService_ClearRun_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_BacktesterService_ClearRun_0(ctx context.Context, marshaler runtime.Marshaler, client BacktesterServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ClearRunRequest + 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_BacktesterService_ClearRun_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ClearRun(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_BacktesterService_ClearRun_0(ctx context.Context, marshaler runtime.Marshaler, server BacktesterServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ClearRunRequest + 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_BacktesterService_ClearRun_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ClearRun(ctx, &protoReq) + return msg, metadata, err + +} + +func request_BacktesterService_ClearAllRuns_0(ctx context.Context, marshaler runtime.Marshaler, client BacktesterServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ClearAllRunsRequest + var metadata runtime.ServerMetadata + + msg, err := client.ClearAllRuns(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_BacktesterService_ClearAllRuns_0(ctx context.Context, marshaler runtime.Marshaler, server BacktesterServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ClearAllRunsRequest + var metadata runtime.ServerMetadata + + msg, err := server.ClearAllRuns(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterBacktesterServiceHandlerServer registers the http handlers for service BacktesterService to "mux". // UnaryRPC :call BacktesterServiceServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterBacktesterServiceHandlerFromEndpoint instead. func RegisterBacktesterServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server BacktesterServiceServer) error { - mux.Handle("GET", pattern_BacktesterService_ExecuteStrategyFromFile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_BacktesterService_ExecuteStrategyFromFile_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/ExecuteStrategyFromFile", runtime.WithHTTPPathPattern("/v1/executestrategyfromfile")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BacktesterService_ExecuteStrategyFromFile_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_ExecuteStrategyFromFile_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_ExecuteStrategyFromConfig_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/ExecuteStrategyFromConfig", runtime.WithHTTPPathPattern("/v1/executestrategyfromconfig")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BacktesterService_ExecuteStrategyFromConfig_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_ExecuteStrategyFromConfig_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_BacktesterService_ListAllRuns_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/ListAllRuns", runtime.WithHTTPPathPattern("/v1/listallruns")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BacktesterService_ListAllRuns_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_ListAllRuns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_StartRun_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/StartRun", runtime.WithHTTPPathPattern("/v1/startrun")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BacktesterService_StartRun_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_StartRun_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_StartAllRuns_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, "/btrpc.BacktesterService/ExecuteStrategyFromFile", runtime.WithHTTPPathPattern("/v1/executestrategyfromfile")) + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/StartAllRuns", runtime.WithHTTPPathPattern("/v1/startallruns")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_BacktesterService_ExecuteStrategyFromFile_0(ctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_BacktesterService_StartAllRuns_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_StartAllRuns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_StopRun_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/StopRun", runtime.WithHTTPPathPattern("/v1/stoprun")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } + resp, md, err := local_request_BacktesterService_StopRun_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } - forward_BacktesterService_ExecuteStrategyFromFile_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_BacktesterService_StopRun_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_BacktesterService_ExecuteStrategyFromConfig_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_BacktesterService_StopAllRuns_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, "/btrpc.BacktesterService/ExecuteStrategyFromConfig", runtime.WithHTTPPathPattern("/v1/executestrategyfromconfig")) + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/StopAllRuns", runtime.WithHTTPPathPattern("/v1/stopallruns")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := local_request_BacktesterService_ExecuteStrategyFromConfig_0(ctx, inboundMarshaler, server, req, pathParams) + resp, md, err := local_request_BacktesterService_StopAllRuns_0(annotatedContext, 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) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_StopAllRuns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_BacktesterService_ClearRun_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/ClearRun", runtime.WithHTTPPathPattern("/v1/clearrun")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } + resp, md, err := local_request_BacktesterService_ClearRun_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } - forward_BacktesterService_ExecuteStrategyFromConfig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_BacktesterService_ClearRun_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_BacktesterService_ClearAllRuns_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/btrpc.BacktesterService/ClearAllRuns", runtime.WithHTTPPathPattern("/v1/clearallruns")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_BacktesterService_ClearAllRuns_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_ClearAllRuns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -198,45 +555,201 @@ func RegisterBacktesterServiceHandler(ctx context.Context, mux *runtime.ServeMux // "BacktesterServiceClient" to call the correct interceptors. func RegisterBacktesterServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client BacktesterServiceClient) error { - mux.Handle("GET", pattern_BacktesterService_ExecuteStrategyFromFile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("POST", pattern_BacktesterService_ExecuteStrategyFromFile_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, "/btrpc.BacktesterService/ExecuteStrategyFromFile", runtime.WithHTTPPathPattern("/v1/executestrategyfromfile")) + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/ExecuteStrategyFromFile", runtime.WithHTTPPathPattern("/v1/executestrategyfromfile")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_BacktesterService_ExecuteStrategyFromFile_0(ctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_BacktesterService_ExecuteStrategyFromFile_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_ExecuteStrategyFromFile_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_ExecuteStrategyFromConfig_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/ExecuteStrategyFromConfig", runtime.WithHTTPPathPattern("/v1/executestrategyfromconfig")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } + resp, md, err := request_BacktesterService_ExecuteStrategyFromConfig_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } - forward_BacktesterService_ExecuteStrategyFromFile_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_BacktesterService_ExecuteStrategyFromConfig_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) - mux.Handle("GET", pattern_BacktesterService_ExecuteStrategyFromConfig_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + mux.Handle("GET", pattern_BacktesterService_ListAllRuns_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, "/btrpc.BacktesterService/ExecuteStrategyFromConfig", runtime.WithHTTPPathPattern("/v1/executestrategyfromconfig")) + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/ListAllRuns", runtime.WithHTTPPathPattern("/v1/listallruns")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } - resp, md, err := request_BacktesterService_ExecuteStrategyFromConfig_0(ctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) + resp, md, err := request_BacktesterService_ListAllRuns_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_ListAllRuns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_StartRun_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/StartRun", runtime.WithHTTPPathPattern("/v1/startrun")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return } + resp, md, err := request_BacktesterService_StartRun_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_StartRun_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_StartAllRuns_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/StartAllRuns", runtime.WithHTTPPathPattern("/v1/startallruns")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BacktesterService_StartAllRuns_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_StartAllRuns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_StopRun_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/StopRun", runtime.WithHTTPPathPattern("/v1/stoprun")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BacktesterService_StopRun_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_StopRun_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("POST", pattern_BacktesterService_StopAllRuns_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/StopAllRuns", runtime.WithHTTPPathPattern("/v1/stopallruns")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BacktesterService_StopAllRuns_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_StopAllRuns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_BacktesterService_ClearRun_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/ClearRun", runtime.WithHTTPPathPattern("/v1/clearrun")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BacktesterService_ClearRun_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_BacktesterService_ClearRun_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("DELETE", pattern_BacktesterService_ClearAllRuns_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 + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/btrpc.BacktesterService/ClearAllRuns", runtime.WithHTTPPathPattern("/v1/clearallruns")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_BacktesterService_ClearAllRuns_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } - forward_BacktesterService_ExecuteStrategyFromConfig_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + forward_BacktesterService_ClearAllRuns_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) }) @@ -247,10 +760,38 @@ var ( pattern_BacktesterService_ExecuteStrategyFromFile_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "executestrategyfromfile"}, "")) pattern_BacktesterService_ExecuteStrategyFromConfig_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "executestrategyfromconfig"}, "")) + + pattern_BacktesterService_ListAllRuns_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "listallruns"}, "")) + + pattern_BacktesterService_StartRun_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "startrun"}, "")) + + pattern_BacktesterService_StartAllRuns_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "startallruns"}, "")) + + pattern_BacktesterService_StopRun_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "stoprun"}, "")) + + pattern_BacktesterService_StopAllRuns_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "stopallruns"}, "")) + + pattern_BacktesterService_ClearRun_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "clearrun"}, "")) + + pattern_BacktesterService_ClearAllRuns_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "clearallruns"}, "")) ) var ( forward_BacktesterService_ExecuteStrategyFromFile_0 = runtime.ForwardResponseMessage forward_BacktesterService_ExecuteStrategyFromConfig_0 = runtime.ForwardResponseMessage + + forward_BacktesterService_ListAllRuns_0 = runtime.ForwardResponseMessage + + forward_BacktesterService_StartRun_0 = runtime.ForwardResponseMessage + + forward_BacktesterService_StartAllRuns_0 = runtime.ForwardResponseMessage + + forward_BacktesterService_StopRun_0 = runtime.ForwardResponseMessage + + forward_BacktesterService_StopAllRuns_0 = runtime.ForwardResponseMessage + + forward_BacktesterService_ClearRun_0 = runtime.ForwardResponseMessage + + forward_BacktesterService_ClearAllRuns_0 = runtime.ForwardResponseMessage ) diff --git a/backtester/btrpc/btrpc.proto b/backtester/btrpc/btrpc.proto index 0548a8b0a01..5ad2ba1a766 100644 --- a/backtester/btrpc/btrpc.proto +++ b/backtester/btrpc/btrpc.proto @@ -171,29 +171,127 @@ message Config { StatisticSettings statistic_settings = 8; } +message RunSummary { + string id = 1; + string strategy_name = 2; + string date_loaded = 3; + string date_started = 4; + string date_ended = 5; + bool closed = 6; + bool live_testing = 7; + bool real_orders = 8; +} + // Requests and responses message ExecuteStrategyFromFileRequest { string strategy_file_path = 1; + bool do_not_run_immediately = 2; + bool do_not_store = 3; } message ExecuteStrategyResponse { - bool success = 1; - string message = 2; + RunSummary run = 1; } message ExecuteStrategyFromConfigRequest { - btrpc.Config config = 1; + bool do_not_run_immediately = 1; + bool do_not_store = 2; + btrpc.Config config = 3; +} + +message ListAllRunsRequest {} + +message ListAllRunsResponse { + repeated RunSummary runs = 1; +} + +message StopRunRequest { + string id = 1; +} + +message StopRunResponse { + RunSummary stopped_run = 1; +} + +message StartRunRequest { + string id = 1; +} + +message StartRunResponse { + bool started = 1; +} + +message StartAllRunsRequest {} + +message StartAllRunsResponse { + repeated string runs_started = 1; +} + +message StopAllRunsRequest {} + +message StopAllRunsResponse { + repeated RunSummary runs_stopped = 1; +} + +message ClearRunRequest { + string id = 1; +} + +message ClearRunResponse { + RunSummary cleared_run = 1; +} + +message ClearAllRunsRequest {} + +message ClearAllRunsResponse { + repeated RunSummary cleared_runs = 1; + repeated RunSummary remaining_runs = 2; } service BacktesterService { rpc ExecuteStrategyFromFile(ExecuteStrategyFromFileRequest) returns (ExecuteStrategyResponse) { option (google.api.http) = { - get: "/v1/executestrategyfromfile" + post: "/v1/executestrategyfromfile" }; } rpc ExecuteStrategyFromConfig(ExecuteStrategyFromConfigRequest) returns (ExecuteStrategyResponse) { option (google.api.http) = { - get: "/v1/executestrategyfromconfig" + post: "/v1/executestrategyfromconfig" + }; + } + rpc ListAllRuns(ListAllRunsRequest) returns (ListAllRunsResponse) { + option (google.api.http) = { + get: "/v1/listallruns" + }; + } + rpc StartRun(StartRunRequest) returns (StartRunResponse) { + option (google.api.http) = { + post: "/v1/startrun" + }; + } + rpc StartAllRuns(StartAllRunsRequest) returns (StartAllRunsResponse) { + option (google.api.http) = { + post: "/v1/startallruns" + }; + } + rpc StopRun(StopRunRequest) returns (StopRunResponse) { + option (google.api.http) = { + post: "/v1/stoprun" + }; + } + rpc StopAllRuns(StopAllRunsRequest) returns (StopAllRunsResponse) { + option (google.api.http) = { + post: "/v1/stopallruns" + }; + } + rpc ClearRun(ClearRunRequest) returns (ClearRunResponse) { + option (google.api.http) = { + delete: "/v1/clearrun" + }; + } + rpc ClearAllRuns(ClearAllRunsRequest) returns (ClearAllRunsResponse) { + option (google.api.http) = { + delete: "/v1/clearallruns" }; } } diff --git a/backtester/btrpc/btrpc.swagger.json b/backtester/btrpc/btrpc.swagger.json index 3ecdb4512df..1b5e6e9e57b 100644 --- a/backtester/btrpc/btrpc.swagger.json +++ b/backtester/btrpc/btrpc.swagger.json @@ -16,8 +16,60 @@ "application/json" ], "paths": { + "/v1/clearallruns": { + "delete": { + "operationId": "BacktesterService_ClearAllRuns", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/btrpcClearAllRunsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "BacktesterService" + ] + } + }, + "/v1/clearrun": { + "delete": { + "operationId": "BacktesterService_ClearRun", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/btrpcClearRunResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "BacktesterService" + ] + } + }, "/v1/executestrategyfromconfig": { - "get": { + "post": { "operationId": "BacktesterService_ExecuteStrategyFromConfig", "responses": { "200": { @@ -34,6 +86,18 @@ } }, "parameters": [ + { + "name": "doNotRunImmediately", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "doNotStore", + "in": "query", + "required": false, + "type": "boolean" + }, { "name": "config.nickname", "in": "query", @@ -299,7 +363,7 @@ } }, "/v1/executestrategyfromfile": { - "get": { + "post": { "operationId": "BacktesterService_ExecuteStrategyFromFile", "responses": { "200": { @@ -321,6 +385,144 @@ "in": "query", "required": false, "type": "string" + }, + { + "name": "doNotRunImmediately", + "in": "query", + "required": false, + "type": "boolean" + }, + { + "name": "doNotStore", + "in": "query", + "required": false, + "type": "boolean" + } + ], + "tags": [ + "BacktesterService" + ] + } + }, + "/v1/listallruns": { + "get": { + "operationId": "BacktesterService_ListAllRuns", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/btrpcListAllRunsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "BacktesterService" + ] + } + }, + "/v1/startallruns": { + "post": { + "operationId": "BacktesterService_StartAllRuns", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/btrpcStartAllRunsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "BacktesterService" + ] + } + }, + "/v1/startrun": { + "post": { + "operationId": "BacktesterService_StartRun", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/btrpcStartRunResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "BacktesterService" + ] + } + }, + "/v1/stopallruns": { + "post": { + "operationId": "BacktesterService_StopAllRuns", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/btrpcStopAllRunsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "tags": [ + "BacktesterService" + ] + } + }, + "/v1/stoprun": { + "post": { + "operationId": "BacktesterService_StopRun", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/btrpcStopRunResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "type": "string" } ], "tags": [ @@ -354,6 +556,31 @@ } } }, + "btrpcClearAllRunsResponse": { + "type": "object", + "properties": { + "clearedRuns": { + "type": "array", + "items": { + "$ref": "#/definitions/btrpcRunSummary" + } + }, + "remainingRuns": { + "type": "array", + "items": { + "$ref": "#/definitions/btrpcRunSummary" + } + } + } + }, + "btrpcClearRunResponse": { + "type": "object", + "properties": { + "clearedRun": { + "$ref": "#/definitions/btrpcRunSummary" + } + } + }, "btrpcConfig": { "type": "object", "properties": { @@ -560,11 +787,8 @@ "btrpcExecuteStrategyResponse": { "type": "object", "properties": { - "success": { - "type": "boolean" - }, - "message": { - "type": "string" + "run": { + "$ref": "#/definitions/btrpcRunSummary" } } }, @@ -607,6 +831,17 @@ } } }, + "btrpcListAllRunsResponse": { + "type": "object", + "properties": { + "runs": { + "type": "array", + "items": { + "$ref": "#/definitions/btrpcRunSummary" + } + } + } + }, "btrpcLiveData": { "type": "object", "properties": { @@ -658,6 +893,35 @@ } } }, + "btrpcRunSummary": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "strategyName": { + "type": "string" + }, + "dateLoaded": { + "type": "string" + }, + "dateStarted": { + "type": "string" + }, + "dateEnded": { + "type": "string" + }, + "closed": { + "type": "boolean" + }, + "liveTesting": { + "type": "boolean" + }, + "realOrders": { + "type": "boolean" + } + } + }, "btrpcSpotDetails": { "type": "object", "properties": { @@ -669,6 +933,25 @@ } } }, + "btrpcStartAllRunsResponse": { + "type": "object", + "properties": { + "runsStarted": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "btrpcStartRunResponse": { + "type": "object", + "properties": { + "started": { + "type": "boolean" + } + } + }, "btrpcStatisticSettings": { "type": "object", "properties": { @@ -677,6 +960,25 @@ } } }, + "btrpcStopAllRunsResponse": { + "type": "object", + "properties": { + "runsStopped": { + "type": "array", + "items": { + "$ref": "#/definitions/btrpcRunSummary" + } + } + } + }, + "btrpcStopRunResponse": { + "type": "object", + "properties": { + "stoppedRun": { + "$ref": "#/definitions/btrpcRunSummary" + } + } + }, "btrpcStrategySettings": { "type": "object", "properties": { diff --git a/backtester/btrpc/btrpc_grpc.pb.go b/backtester/btrpc/btrpc_grpc.pb.go index 7381cf316f2..8fc4b7c8cbb 100644 --- a/backtester/btrpc/btrpc_grpc.pb.go +++ b/backtester/btrpc/btrpc_grpc.pb.go @@ -24,6 +24,13 @@ const _ = grpc.SupportPackageIsVersion7 type BacktesterServiceClient interface { ExecuteStrategyFromFile(ctx context.Context, in *ExecuteStrategyFromFileRequest, opts ...grpc.CallOption) (*ExecuteStrategyResponse, error) ExecuteStrategyFromConfig(ctx context.Context, in *ExecuteStrategyFromConfigRequest, opts ...grpc.CallOption) (*ExecuteStrategyResponse, error) + ListAllRuns(ctx context.Context, in *ListAllRunsRequest, opts ...grpc.CallOption) (*ListAllRunsResponse, error) + StartRun(ctx context.Context, in *StartRunRequest, opts ...grpc.CallOption) (*StartRunResponse, error) + StartAllRuns(ctx context.Context, in *StartAllRunsRequest, opts ...grpc.CallOption) (*StartAllRunsResponse, error) + StopRun(ctx context.Context, in *StopRunRequest, opts ...grpc.CallOption) (*StopRunResponse, error) + StopAllRuns(ctx context.Context, in *StopAllRunsRequest, opts ...grpc.CallOption) (*StopAllRunsResponse, error) + ClearRun(ctx context.Context, in *ClearRunRequest, opts ...grpc.CallOption) (*ClearRunResponse, error) + ClearAllRuns(ctx context.Context, in *ClearAllRunsRequest, opts ...grpc.CallOption) (*ClearAllRunsResponse, error) } type backtesterServiceClient struct { @@ -52,12 +59,82 @@ func (c *backtesterServiceClient) ExecuteStrategyFromConfig(ctx context.Context, return out, nil } +func (c *backtesterServiceClient) ListAllRuns(ctx context.Context, in *ListAllRunsRequest, opts ...grpc.CallOption) (*ListAllRunsResponse, error) { + out := new(ListAllRunsResponse) + err := c.cc.Invoke(ctx, "/btrpc.BacktesterService/ListAllRuns", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backtesterServiceClient) StartRun(ctx context.Context, in *StartRunRequest, opts ...grpc.CallOption) (*StartRunResponse, error) { + out := new(StartRunResponse) + err := c.cc.Invoke(ctx, "/btrpc.BacktesterService/StartRun", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backtesterServiceClient) StartAllRuns(ctx context.Context, in *StartAllRunsRequest, opts ...grpc.CallOption) (*StartAllRunsResponse, error) { + out := new(StartAllRunsResponse) + err := c.cc.Invoke(ctx, "/btrpc.BacktesterService/StartAllRuns", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backtesterServiceClient) StopRun(ctx context.Context, in *StopRunRequest, opts ...grpc.CallOption) (*StopRunResponse, error) { + out := new(StopRunResponse) + err := c.cc.Invoke(ctx, "/btrpc.BacktesterService/StopRun", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backtesterServiceClient) StopAllRuns(ctx context.Context, in *StopAllRunsRequest, opts ...grpc.CallOption) (*StopAllRunsResponse, error) { + out := new(StopAllRunsResponse) + err := c.cc.Invoke(ctx, "/btrpc.BacktesterService/StopAllRuns", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backtesterServiceClient) ClearRun(ctx context.Context, in *ClearRunRequest, opts ...grpc.CallOption) (*ClearRunResponse, error) { + out := new(ClearRunResponse) + err := c.cc.Invoke(ctx, "/btrpc.BacktesterService/ClearRun", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *backtesterServiceClient) ClearAllRuns(ctx context.Context, in *ClearAllRunsRequest, opts ...grpc.CallOption) (*ClearAllRunsResponse, error) { + out := new(ClearAllRunsResponse) + err := c.cc.Invoke(ctx, "/btrpc.BacktesterService/ClearAllRuns", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // BacktesterServiceServer is the server API for BacktesterService service. // All implementations must embed UnimplementedBacktesterServiceServer // for forward compatibility type BacktesterServiceServer interface { ExecuteStrategyFromFile(context.Context, *ExecuteStrategyFromFileRequest) (*ExecuteStrategyResponse, error) ExecuteStrategyFromConfig(context.Context, *ExecuteStrategyFromConfigRequest) (*ExecuteStrategyResponse, error) + ListAllRuns(context.Context, *ListAllRunsRequest) (*ListAllRunsResponse, error) + StartRun(context.Context, *StartRunRequest) (*StartRunResponse, error) + StartAllRuns(context.Context, *StartAllRunsRequest) (*StartAllRunsResponse, error) + StopRun(context.Context, *StopRunRequest) (*StopRunResponse, error) + StopAllRuns(context.Context, *StopAllRunsRequest) (*StopAllRunsResponse, error) + ClearRun(context.Context, *ClearRunRequest) (*ClearRunResponse, error) + ClearAllRuns(context.Context, *ClearAllRunsRequest) (*ClearAllRunsResponse, error) mustEmbedUnimplementedBacktesterServiceServer() } @@ -71,6 +148,27 @@ func (UnimplementedBacktesterServiceServer) ExecuteStrategyFromFile(context.Cont func (UnimplementedBacktesterServiceServer) ExecuteStrategyFromConfig(context.Context, *ExecuteStrategyFromConfigRequest) (*ExecuteStrategyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ExecuteStrategyFromConfig not implemented") } +func (UnimplementedBacktesterServiceServer) ListAllRuns(context.Context, *ListAllRunsRequest) (*ListAllRunsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListAllRuns not implemented") +} +func (UnimplementedBacktesterServiceServer) StartRun(context.Context, *StartRunRequest) (*StartRunResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StartRun not implemented") +} +func (UnimplementedBacktesterServiceServer) StartAllRuns(context.Context, *StartAllRunsRequest) (*StartAllRunsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StartAllRuns not implemented") +} +func (UnimplementedBacktesterServiceServer) StopRun(context.Context, *StopRunRequest) (*StopRunResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StopRun not implemented") +} +func (UnimplementedBacktesterServiceServer) StopAllRuns(context.Context, *StopAllRunsRequest) (*StopAllRunsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StopAllRuns not implemented") +} +func (UnimplementedBacktesterServiceServer) ClearRun(context.Context, *ClearRunRequest) (*ClearRunResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClearRun not implemented") +} +func (UnimplementedBacktesterServiceServer) ClearAllRuns(context.Context, *ClearAllRunsRequest) (*ClearAllRunsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClearAllRuns not implemented") +} func (UnimplementedBacktesterServiceServer) mustEmbedUnimplementedBacktesterServiceServer() {} // UnsafeBacktesterServiceServer may be embedded to opt out of forward compatibility for this service. @@ -120,6 +218,132 @@ func _BacktesterService_ExecuteStrategyFromConfig_Handler(srv interface{}, ctx c return interceptor(ctx, in, info, handler) } +func _BacktesterService_ListAllRuns_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListAllRunsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BacktesterServiceServer).ListAllRuns(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/btrpc.BacktesterService/ListAllRuns", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BacktesterServiceServer).ListAllRuns(ctx, req.(*ListAllRunsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BacktesterService_StartRun_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartRunRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BacktesterServiceServer).StartRun(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/btrpc.BacktesterService/StartRun", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BacktesterServiceServer).StartRun(ctx, req.(*StartRunRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BacktesterService_StartAllRuns_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StartAllRunsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BacktesterServiceServer).StartAllRuns(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/btrpc.BacktesterService/StartAllRuns", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BacktesterServiceServer).StartAllRuns(ctx, req.(*StartAllRunsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BacktesterService_StopRun_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopRunRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BacktesterServiceServer).StopRun(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/btrpc.BacktesterService/StopRun", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BacktesterServiceServer).StopRun(ctx, req.(*StopRunRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BacktesterService_StopAllRuns_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StopAllRunsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BacktesterServiceServer).StopAllRuns(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/btrpc.BacktesterService/StopAllRuns", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BacktesterServiceServer).StopAllRuns(ctx, req.(*StopAllRunsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BacktesterService_ClearRun_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ClearRunRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BacktesterServiceServer).ClearRun(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/btrpc.BacktesterService/ClearRun", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BacktesterServiceServer).ClearRun(ctx, req.(*ClearRunRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BacktesterService_ClearAllRuns_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ClearAllRunsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BacktesterServiceServer).ClearAllRuns(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/btrpc.BacktesterService/ClearAllRuns", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BacktesterServiceServer).ClearAllRuns(ctx, req.(*ClearAllRunsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // BacktesterService_ServiceDesc is the grpc.ServiceDesc for BacktesterService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -135,6 +359,34 @@ var BacktesterService_ServiceDesc = grpc.ServiceDesc{ MethodName: "ExecuteStrategyFromConfig", Handler: _BacktesterService_ExecuteStrategyFromConfig_Handler, }, + { + MethodName: "ListAllRuns", + Handler: _BacktesterService_ListAllRuns_Handler, + }, + { + MethodName: "StartRun", + Handler: _BacktesterService_StartRun_Handler, + }, + { + MethodName: "StartAllRuns", + Handler: _BacktesterService_StartAllRuns_Handler, + }, + { + MethodName: "StopRun", + Handler: _BacktesterService_StopRun_Handler, + }, + { + MethodName: "StopAllRuns", + Handler: _BacktesterService_StopAllRuns_Handler, + }, + { + MethodName: "ClearRun", + Handler: _BacktesterService_ClearRun_Handler, + }, + { + MethodName: "ClearAllRuns", + Handler: _BacktesterService_ClearAllRuns_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "btrpc.proto", diff --git a/backtester/engine/backtest.go b/backtester/engine/backtest.go index c52390f8ea6..1b31e628bdb 100644 --- a/backtester/engine/backtest.go +++ b/backtester/engine/backtest.go @@ -3,7 +3,10 @@ package engine import ( "errors" "fmt" + "sync" + "time" + "github.com/gofrs/uuid" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" @@ -17,6 +20,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/order" "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" gctexchange "github.com/thrasher-corp/gocryptotrader/exchanges" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" @@ -25,12 +29,17 @@ import ( ) // New returns a new BackTest instance -func New() *BackTest { - return &BackTest{ +func New() (*BackTest, error) { + bt := &BackTest{ shutdown: make(chan struct{}), Datas: &data.HandlerPerCurrency{}, EventQueue: &eventholder.Holder{}, } + err := bt.SetupMetaData() + if err != nil { + return nil, err + } + return bt, nil } // Reset BackTest values to default @@ -46,9 +55,73 @@ func (bt *BackTest) Reset() { bt.databaseManager = nil } +// ExecuteStrategy executes the strategy using the provided configs +func (bt *BackTest) ExecuteStrategy(waitForOfflineCompletion bool) error { + if bt == nil { + return gctcommon.ErrNilPointer + } + bt.m.Lock() + if bt.MetaData.DateLoaded.IsZero() { + bt.m.Unlock() + return errNotSetup + } + if !bt.MetaData.Closed && !bt.MetaData.DateStarted.IsZero() { + bt.m.Unlock() + return fmt.Errorf("%w %v %v", errRunIsRunning, bt.MetaData.ID, bt.MetaData.Strategy) + } + if bt.MetaData.Closed { + bt.m.Unlock() + return fmt.Errorf("%w %v %v", errAlreadyRan, bt.MetaData.ID, bt.MetaData.Strategy) + } + + bt.MetaData.DateStarted = time.Now() + liveTesting := bt.MetaData.LiveTesting + bt.m.Unlock() + var wg sync.WaitGroup + if waitForOfflineCompletion { + wg.Add(1) + } + go func() { + if waitForOfflineCompletion { + defer wg.Done() + } + if liveTesting { + if waitForOfflineCompletion { + log.Errorf(common.Backtester, "%v cannot wait for completion of a live test", errCannotHandleRequest) + return + } + err := bt.RunLive() + if err != nil { + log.Error(log.Global, err) + } + } else { + bt.Run() + close(bt.shutdown) + bt.m.Lock() + bt.MetaData.Closed = true + bt.MetaData.DateEnded = time.Now() + bt.m.Unlock() + err := bt.Statistic.CalculateAllResults() + if err != nil { + log.Error(log.Global, err) + return + } + err = bt.Reports.GenerateReport() + if err != nil { + log.Error(log.Global, err) + } + } + }() + wg.Wait() + return nil +} + // Run will iterate over loaded data events // save them and then handle the event based on its type func (bt *BackTest) Run() { + if bt.MetaData.DateLoaded.IsZero() { + return + } log.Info(common.Backtester, "Running backtester against pre-defined data") dataLoadingIssue: for ev := bt.EventQueue.NextEvent(); ; ev = bt.EventQueue.NextEvent() { @@ -469,5 +542,109 @@ func (bt *BackTest) processFuturesFillEvent(ev fill.Event, funds funding.IFundRe // Stop shuts down the live data loop func (bt *BackTest) Stop() { + if bt == nil { + return + } + bt.m.Lock() + defer bt.m.Unlock() + if bt.MetaData.Closed { + return + } close(bt.shutdown) + bt.MetaData.Closed = true + bt.MetaData.DateEnded = time.Now() + err := bt.Statistic.CalculateAllResults() + if err != nil { + log.Error(log.Global, err) + return + } + err = bt.Reports.GenerateReport() + if err != nil { + log.Error(log.Global, err) + } +} + +// GenerateSummary creates a summary of a backtesting/livestrategy run +// this summary contains many details of a run +func (bt *BackTest) GenerateSummary() (*RunSummary, error) { + if bt == nil { + return nil, gctcommon.ErrNilPointer + } + bt.m.Lock() + defer bt.m.Unlock() + return &RunSummary{ + MetaData: bt.MetaData, + }, nil +} + +// SetupMetaData will populate metadata fields +func (bt *BackTest) SetupMetaData() error { + if bt == nil { + return gctcommon.ErrNilPointer + } + bt.m.Lock() + defer bt.m.Unlock() + if !bt.MetaData.ID.IsNil() && !bt.MetaData.DateLoaded.IsZero() { + // already setup + return nil + } + id, err := uuid.NewV4() + if err != nil { + return err + } + bt.MetaData.ID = id + bt.MetaData.DateLoaded = time.Now() + return nil +} + +// IsRunning checks if the run is running +func (bt *BackTest) IsRunning() bool { + if bt == nil { + return false + } + bt.m.Lock() + defer bt.m.Unlock() + return !bt.MetaData.DateStarted.IsZero() && !bt.MetaData.Closed +} + +// HasRan checks if the run has been ran +func (bt *BackTest) HasRan() bool { + if bt == nil { + return false + } + bt.m.Lock() + defer bt.m.Unlock() + return bt.MetaData.Closed +} + +// Equal checks if the incoming run matches +func (bt *BackTest) Equal(bt2 *BackTest) bool { + if bt == nil || bt2 == nil { + return false + } + bt.m.Lock() + btM := bt.MetaData + bt.m.Unlock() + // if they are actually the same pointer + // locks must be handled separately + bt2.m.Lock() + btM2 := bt2.MetaData + bt2.m.Unlock() + return btM == btM2 +} + +// MatchesID checks if the backtesting run's ID matches the supplied +func (bt *BackTest) MatchesID(id uuid.UUID) bool { + if bt == nil { + return false + } + if id.IsNil() { + return false + } + bt.m.Lock() + defer bt.m.Unlock() + if bt.MetaData.ID.IsNil() { + return false + } + return bt.MetaData.ID == id } diff --git a/backtester/engine/backtest_test.go b/backtester/engine/backtest_test.go index 96a6db1d65c..527b635c769 100644 --- a/backtester/engine/backtest_test.go +++ b/backtester/engine/backtest_test.go @@ -2,11 +2,14 @@ package engine import ( "errors" + "path/filepath" "testing" "time" + "github.com/gofrs/uuid" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/common" + "github.com/thrasher-corp/gocryptotrader/backtester/config" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/data/kline" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" @@ -24,6 +27,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/eventtypes/signal" "github.com/thrasher-corp/gocryptotrader/backtester/funding" "github.com/thrasher-corp/gocryptotrader/backtester/report" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/engine" "github.com/thrasher-corp/gocryptotrader/exchanges/asset" @@ -180,7 +184,19 @@ func TestFullCycle(t *testing.T) { func TestStop(t *testing.T) { t.Parallel() - bt := BackTest{shutdown: make(chan struct{})} + bt := &BackTest{ + shutdown: make(chan struct{}), + Statistic: &statistics.Statistic{}, + } + bt.Stop() + tt := bt.MetaData.DateEnded + + bt.Stop() + if !tt.Equal(bt.MetaData.DateEnded) { + t.Errorf("received '%v' expected '%v'", bt.MetaData.DateEnded, tt) + } + + bt = nil bt.Stop() } @@ -965,3 +981,232 @@ func TestProcessFuturesFillEvent(t *testing.T) { t.Errorf("received '%v' expected '%v'", err, expectedError) } } + +func TestGenerateSummary(t *testing.T) { + t.Parallel() + bt := &BackTest{} + sum, err := bt.GenerateSummary() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if !sum.MetaData.ID.IsNil() { + t.Errorf("received '%v' expected '%v'", sum.MetaData.ID, "") + } + id, err := uuid.NewV4() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + bt.MetaData.ID = id + sum, err = bt.GenerateSummary() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if sum.MetaData.ID != id { + t.Errorf("received '%v' expected '%v'", sum.MetaData.ID, id) + } + + bt = nil + _, err = bt.GenerateSummary() + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestSetupMetaData(t *testing.T) { + t.Parallel() + bt := &BackTest{} + err := bt.SetupMetaData() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if bt.MetaData.ID.IsNil() { + t.Errorf("received '%v' expected '%v'", bt.MetaData.ID, "an ID") + } + firstID := bt.MetaData.ID + err = bt.SetupMetaData() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if bt.MetaData.ID != firstID { + t.Errorf("received '%v' expected '%v'", bt.MetaData.ID, firstID) + } + + bt = nil + err = bt.SetupMetaData() + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestIsRunning(t *testing.T) { + t.Parallel() + bt := &BackTest{} + if bt.IsRunning() { + t.Errorf("received '%v' expected '%v'", true, false) + } + + bt.MetaData.DateStarted = time.Now() + if !bt.IsRunning() { + t.Errorf("received '%v' expected '%v'", false, true) + } + + bt.MetaData.Closed = true + if bt.IsRunning() { + t.Errorf("received '%v' expected '%v'", true, false) + } + + bt = nil + if bt.IsRunning() { + t.Errorf("received '%v' expected '%v'", true, false) + } +} + +func TestHasRan(t *testing.T) { + t.Parallel() + bt := &BackTest{} + if bt.HasRan() { + t.Errorf("received '%v' expected '%v'", true, false) + } + + bt.MetaData.DateStarted = time.Now() + if bt.HasRan() { + t.Errorf("received '%v' expected '%v'", false, true) + } + + bt.MetaData.Closed = true + if !bt.HasRan() { + t.Errorf("received '%v' expected '%v'", true, false) + } + + bt = nil + if bt.HasRan() { + t.Errorf("received '%v' expected '%v'", true, false) + } +} + +func TestEqual(t *testing.T) { + t.Parallel() + bt := &BackTest{} + bt2 := &BackTest{} + bt3 := &BackTest{} + if !bt.Equal(bt2) { + t.Errorf("received '%v' expected '%v'", false, true) + } + + err := bt.SetupMetaData() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + bt2.MetaData = bt.MetaData + if !bt.Equal(bt2) { + t.Errorf("received '%v' expected '%v'", false, true) + } + + if bt.Equal(nil) { + t.Errorf("received '%v' expected '%v'", true, false) + } + + err = bt3.SetupMetaData() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if bt.Equal(bt3) { + t.Errorf("received '%v' expected '%v'", true, false) + } + + bt = nil + if bt.Equal(bt2) { + t.Errorf("received '%v' expected '%v'", true, false) + } +} + +func TestMatchesID(t *testing.T) { + t.Parallel() + bt := &BackTest{} + if bt.MatchesID(uuid.Nil) { + t.Errorf("received '%v' expected '%v'", true, false) + } + + err := bt.SetupMetaData() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + if bt.MatchesID(uuid.Nil) { + t.Errorf("received '%v' expected '%v'", true, false) + } + + if !bt.MatchesID(bt.MetaData.ID) { + t.Errorf("received '%v' expected '%v'", false, true) + } + + id := bt.MetaData.ID + bt.MetaData.ID = uuid.Nil + if bt.MatchesID(id) { + t.Errorf("received '%v' expected '%v'", true, false) + } + + bt = nil + if bt.MatchesID(id) { + t.Errorf("received '%v' expected '%v'", true, false) + } +} + +func TestExecuteStrategy(t *testing.T) { + t.Parallel() + bt := &BackTest{} + err := bt.ExecuteStrategy(false) + if !errors.Is(err, errNotSetup) { + t.Errorf("received '%v' expected '%v'", err, errNotSetup) + } + bt.MetaData.DateLoaded = time.Now() + bt.MetaData.DateStarted = time.Now() + err = bt.ExecuteStrategy(false) + if !errors.Is(err, errRunIsRunning) { + t.Errorf("received '%v' expected '%v'", err, errRunIsRunning) + } + + strat1 := filepath.Join("..", "config", "strategyexamples", "dca-api-candles.strat") + cfg, err := config.ReadStrategyConfigFromFile(strat1) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + bt, err = NewFromConfig(cfg, "", "", false) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + bt.Stop() + + err = bt.ExecuteStrategy(true) + if !errors.Is(err, errAlreadyRan) { + t.Errorf("received '%v' expected '%v'", err, errAlreadyRan) + } + + strat2 := filepath.Join("..", "config", "strategyexamples", "dca-candles-live.strat") + cfg, err = config.ReadStrategyConfigFromFile(strat2) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + bt, err = NewFromConfig(cfg, "", "", false) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = bt.ExecuteStrategy(true) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + bt.MetaData.DateStarted = time.Time{} + err = bt.ExecuteStrategy(false) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + bt.Stop() + + bt = nil + err = bt.ExecuteStrategy(false) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} diff --git a/backtester/engine/backtest_types.go b/backtester/engine/backtest_types.go index a593e147263..04f7a2ca64f 100644 --- a/backtester/engine/backtest_types.go +++ b/backtester/engine/backtest_types.go @@ -2,7 +2,10 @@ package engine import ( "errors" + "sync" + "time" + "github.com/gofrs/uuid" "github.com/thrasher-corp/gocryptotrader/backtester/data" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/exchange" @@ -24,11 +27,14 @@ var ( errNilData = errors.New("nil data received") errNilExchange = errors.New("nil exchange received") errLiveUSDTrackingNotSupported = errors.New("USD tracking not supported for live data") + errNotSetup = errors.New("backtesting run not setup") ) // BackTest is the main holder of all backtesting functionality type BackTest struct { + m sync.Mutex hasHandledEvent bool + MetaData RunMetaData shutdown chan struct{} Datas data.Holder Strategy strategies.Handler @@ -42,3 +48,27 @@ type BackTest struct { orderManager *engine.OrderManager databaseManager *engine.DatabaseConnectionManager } + +// RunSummary holds details of a BackTest +// rather than passing entire contents around +type RunSummary struct { + MetaData RunMetaData +} + +// RunMetaData contains details about a run such as when it was loaded +type RunMetaData struct { + ID uuid.UUID + Strategy string + DateLoaded time.Time + DateStarted time.Time + DateEnded time.Time + Closed bool + LiveTesting bool + RealOrders bool +} + +// RunManager contains all backtesting/livestrategy runs +type RunManager struct { + m sync.Mutex + runs []*BackTest +} diff --git a/backtester/engine/grpcserver.go b/backtester/engine/grpcserver.go index e6335fbd5d3..cb223a2794c 100644 --- a/backtester/engine/grpcserver.go +++ b/backtester/engine/grpcserver.go @@ -10,12 +10,14 @@ import ( "path/filepath" "strings" + "github.com/gofrs/uuid" grpcauth "github.com/grpc-ecosystem/go-grpc-middleware/auth" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/shopspring/decimal" "github.com/thrasher-corp/gocryptotrader/backtester/btrpc" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/config" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/database" @@ -32,30 +34,39 @@ import ( ) var ( - errBadPort = errors.New("received bad port") + errBadPort = errors.New("received bad port") + errCannotHandleRequest = errors.New("cannot handle request") ) // GRPCServer struct type GRPCServer struct { btrpc.BacktesterServiceServer - *config.BacktesterConfig + config *config.BacktesterConfig + manager *RunManager } // SetupRPCServer sets up the gRPC server -func SetupRPCServer(cfg *config.BacktesterConfig) *GRPCServer { - return &GRPCServer{ - BacktesterConfig: cfg, +func SetupRPCServer(cfg *config.BacktesterConfig, manager *RunManager) (*GRPCServer, error) { + if cfg == nil { + return nil, fmt.Errorf("%w backtester config", common.ErrNilArguments) + } + if manager == nil { + return nil, fmt.Errorf("%w run manager", common.ErrNilArguments) } + return &GRPCServer{ + config: cfg, + manager: manager, + }, nil } // StartRPCServer starts a gRPC server with TLS auth func StartRPCServer(server *GRPCServer) error { - targetDir := utils.GetTLSDir(server.GRPC.TLSDir) + targetDir := utils.GetTLSDir(server.config.GRPC.TLSDir) if err := gctengine.CheckCerts(targetDir); err != nil { return err } - log.Debugf(log.GRPCSys, "Backtester GRPC server enabled. Starting GRPC server on https://%v.\n", server.GRPC.ListenAddress) - lis, err := net.Listen("tcp", server.GRPC.ListenAddress) + log.Debugf(log.GRPCSys, "Backtester GRPC server enabled. Starting GRPC server on https://%v.\n", server.config.GRPC.ListenAddress) + lis, err := net.Listen("tcp", server.config.GRPC.ListenAddress) if err != nil { return err } @@ -81,7 +92,7 @@ func StartRPCServer(server *GRPCServer) error { log.Debugln(log.GRPCSys, "GRPC server started!") - if server.GRPC.GRPCProxyEnabled { + if server.config.GRPC.GRPCProxyEnabled { return server.StartRPCRESTProxy() } return nil @@ -89,8 +100,8 @@ func StartRPCServer(server *GRPCServer) error { // StartRPCRESTProxy starts a gRPC proxy func (s *GRPCServer) StartRPCRESTProxy() error { - log.Debugf(log.GRPCSys, "GRPC proxy server support enabled. Starting gRPC proxy server on http://%v.\n", s.GRPC.GRPCProxyListenAddress) - targetDir := utils.GetTLSDir(s.GRPC.TLSDir) + log.Debugf(log.GRPCSys, "GRPC proxy server support enabled. Starting gRPC proxy server on http://%v.\n", s.config.GRPC.GRPCProxyListenAddress) + targetDir := utils.GetTLSDir(s.config.GRPC.TLSDir) creds, err := credentials.NewClientTLSFromFile(filepath.Join(targetDir, "cert.pem"), "") if err != nil { return fmt.Errorf("unabled to start gRPC proxy. Err: %w", err) @@ -99,18 +110,18 @@ func (s *GRPCServer) StartRPCRESTProxy() error { mux := runtime.NewServeMux() opts := []grpc.DialOption{grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(auth.BasicAuth{ - Username: s.GRPC.Username, - Password: s.GRPC.Password, + Username: s.config.GRPC.Username, + Password: s.config.GRPC.Password, }), } err = btrpc.RegisterBacktesterServiceHandlerFromEndpoint(context.Background(), - mux, s.GRPC.ListenAddress, opts) + mux, s.config.GRPC.ListenAddress, opts) if err != nil { return fmt.Errorf("failed to register gRPC proxy. Err: %w", err) } go func() { - if err = http.ListenAndServe(s.GRPC.GRPCProxyListenAddress, mux); err != nil { + if err = http.ListenAndServe(s.config.GRPC.GRPCProxyListenAddress, mux); err != nil { log.Errorf(log.GRPCSys, "GRPC proxy failed to server: %s\n", err) } }() @@ -143,29 +154,93 @@ func (s *GRPCServer) authenticateClient(ctx context.Context) (context.Context, e username := creds[0] password := creds[1] - if username != s.GRPC.Username || - password != s.GRPC.Password { + if username != s.config.GRPC.Username || + password != s.config.GRPC.Password { return ctx, fmt.Errorf("username/password mismatch") } return ctx, nil } +// convertSummary converts a run summary into a RPC format +func convertSummary(run *RunSummary) *btrpc.RunSummary { + runSummary := &btrpc.RunSummary{ + Id: run.MetaData.ID.String(), + StrategyName: run.MetaData.Strategy, + Closed: run.MetaData.Closed, + LiveTesting: run.MetaData.LiveTesting, + RealOrders: run.MetaData.RealOrders, + } + if !run.MetaData.DateStarted.IsZero() { + runSummary.DateStarted = run.MetaData.DateStarted.Format(gctcommon.SimpleTimeFormatWithTimezone) + } + if !run.MetaData.DateLoaded.IsZero() { + runSummary.DateLoaded = run.MetaData.DateLoaded.Format(gctcommon.SimpleTimeFormatWithTimezone) + } + if !run.MetaData.DateEnded.IsZero() { + runSummary.DateEnded = run.MetaData.DateEnded.Format(gctcommon.SimpleTimeFormatWithTimezone) + } + return runSummary +} + // ExecuteStrategyFromFile will backtest a strategy from the filepath provided func (s *GRPCServer) ExecuteStrategyFromFile(_ context.Context, request *btrpc.ExecuteStrategyFromFileRequest) (*btrpc.ExecuteStrategyResponse, error) { + if s.config == nil { + return nil, fmt.Errorf("%w server config", gctcommon.ErrNilPointer) + } + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } if request == nil { return nil, fmt.Errorf("%w nil request", common.ErrNilArguments) } + if request.DoNotRunImmediately && request.DoNotStore { + return nil, fmt.Errorf("%w cannot manage a run with both dnr and dns", errCannotHandleRequest) + } + dir := request.StrategyFilePath cfg, err := config.ReadStrategyConfigFromFile(dir) if err != nil { return nil, err } - err = ExecuteStrategy(cfg, s.BacktesterConfig) + + err = cfg.Validate() + if err != nil { + return nil, err + } + if cfg == nil { + err = fmt.Errorf("%w backtester config", common.ErrNilArguments) + return nil, err + } + + if !s.config.Report.GenerateReport { + s.config.Report.OutputPath = "" + s.config.Report.TemplatePath = "" + } + + bt, err := NewFromConfig(cfg, s.config.Report.TemplatePath, s.config.Report.OutputPath, s.config.Verbose) + if err != nil { + return nil, err + } + + if !request.DoNotStore { + err = s.manager.AddRun(bt) + if err != nil { + return nil, err + } + } + + if !request.DoNotRunImmediately { + err = bt.ExecuteStrategy(false) + if err != nil { + return nil, err + } + } + btSum, err := bt.GenerateSummary() if err != nil { return nil, err } return &btrpc.ExecuteStrategyResponse{ - Success: true, + Run: convertSummary(btSum), }, nil } @@ -173,9 +248,18 @@ func (s *GRPCServer) ExecuteStrategyFromFile(_ context.Context, request *btrpc.E // this should be a preferred method of interacting with backtester, as it allows for very quick // minor tweaks to strategy to determine the best result - SO LONG AS YOU DONT OVERFIT func (s *GRPCServer) ExecuteStrategyFromConfig(_ context.Context, request *btrpc.ExecuteStrategyFromConfigRequest) (*btrpc.ExecuteStrategyResponse, error) { + if s.config == nil { + return nil, fmt.Errorf("%w server config", gctcommon.ErrNilPointer) + } + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } if request == nil || request.Config == nil { return nil, fmt.Errorf("%w nil request", common.ErrNilArguments) } + if request.DoNotRunImmediately && request.DoNotStore { + return nil, fmt.Errorf("%w cannot manage a run with both dnr and dns", errCannotHandleRequest) + } rfr, err := decimal.NewFromString(request.Config.StatisticSettings.RiskFreeRate) if err != nil { @@ -495,11 +579,185 @@ func (s *GRPCServer) ExecuteStrategyFromConfig(_ context.Context, request *btrpc }, } - err = ExecuteStrategy(cfg, s.BacktesterConfig) + if !s.config.Report.GenerateReport { + s.config.Report.OutputPath = "" + s.config.Report.TemplatePath = "" + } + + bt, err := NewFromConfig(cfg, s.config.Report.TemplatePath, s.config.Report.OutputPath, s.config.Verbose) + if err != nil { + return nil, err + } + + if !request.DoNotStore { + err = s.manager.AddRun(bt) + if err != nil { + return nil, err + } + } + + if !request.DoNotRunImmediately { + err = bt.ExecuteStrategy(false) + if err != nil { + return nil, err + } + } + btSum, err := bt.GenerateSummary() if err != nil { return nil, err } return &btrpc.ExecuteStrategyResponse{ - Success: true, + Run: convertSummary(btSum), + }, nil +} + +// ListAllRuns returns all backtesting/livestrategy runs managed by the server +func (s *GRPCServer) ListAllRuns(_ context.Context, _ *btrpc.ListAllRunsRequest) (*btrpc.ListAllRunsResponse, error) { + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } + list, err := s.manager.List() + if err != nil { + return nil, err + } + response := make([]*btrpc.RunSummary, len(list)) + for i := range list { + response[i] = convertSummary(list[i]) + } + return &btrpc.ListAllRunsResponse{ + Runs: response, + }, nil +} + +// StopRun stops a backtest/livestrategy run in its tracks +func (s *GRPCServer) StopRun(_ context.Context, req *btrpc.StopRunRequest) (*btrpc.StopRunResponse, error) { + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } + if req == nil { + return nil, fmt.Errorf("%w StopRunRequest", gctcommon.ErrNilPointer) + } + id, err := uuid.FromString(req.Id) + if err != nil { + return nil, err + } + run, err := s.manager.GetSummary(id) + if err != nil { + return nil, err + } + err = s.manager.StopRun(id) + if err != nil { + return nil, err + } + return &btrpc.StopRunResponse{ + StoppedRun: convertSummary(run), + }, nil +} + +// StopAllRuns stops all backtest/livestrategy runs in its tracks +func (s *GRPCServer) StopAllRuns(_ context.Context, _ *btrpc.StopAllRunsRequest) (*btrpc.StopAllRunsResponse, error) { + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } + stopped, err := s.manager.StopAllRuns() + if err != nil { + return nil, err + } + + stoppedRuns := make([]*btrpc.RunSummary, len(stopped)) + for i := range stopped { + stoppedRuns[i] = convertSummary(stopped[i]) + } + return &btrpc.StopAllRunsResponse{ + RunsStopped: stoppedRuns, + }, nil +} + +// StartRun starts a backtest/livestrategy that was set to not start automatically +func (s *GRPCServer) StartRun(_ context.Context, req *btrpc.StartRunRequest) (*btrpc.StartRunResponse, error) { + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } + if req == nil { + return nil, fmt.Errorf("%w StartRunRequest", gctcommon.ErrNilPointer) + } + id, err := uuid.FromString(req.Id) + if err != nil { + return nil, err + } + err = s.manager.StartRun(id) + if err != nil { + return nil, err + } + return &btrpc.StartRunResponse{ + Started: true, + }, nil +} + +// StartAllRuns starts all backtest/livestrategy runs +func (s *GRPCServer) StartAllRuns(_ context.Context, _ *btrpc.StartAllRunsRequest) (*btrpc.StartAllRunsResponse, error) { + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } + started, err := s.manager.StartAllRuns() + if err != nil { + return nil, err + } + + startedRuns := make([]string, len(started)) + for i := range started { + startedRuns[i] = started[i].String() + } + return &btrpc.StartAllRunsResponse{ + RunsStarted: startedRuns, + }, nil +} + +// ClearRun removes a run from memory, but only if it is not running +func (s *GRPCServer) ClearRun(_ context.Context, req *btrpc.ClearRunRequest) (*btrpc.ClearRunResponse, error) { + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } + if req == nil { + return nil, fmt.Errorf("%w ClearRunRequest", gctcommon.ErrNilPointer) + } + id, err := uuid.FromString(req.Id) + if err != nil { + return nil, err + } + run, err := s.manager.GetSummary(id) + if err != nil { + return nil, err + } + err = s.manager.ClearRun(id) + if err != nil { + return nil, err + } + return &btrpc.ClearRunResponse{ + ClearedRun: convertSummary(run), + }, nil +} + +// ClearAllRuns removes all runs from memory, but only if they are not running +func (s *GRPCServer) ClearAllRuns(_ context.Context, _ *btrpc.ClearAllRunsRequest) (*btrpc.ClearAllRunsResponse, error) { + if s.manager == nil { + return nil, fmt.Errorf("%w run manager", gctcommon.ErrNilPointer) + } + clearedRuns, remainingRuns, err := s.manager.ClearAllRuns() + if err != nil { + return nil, err + } + + clearedResponse := make([]*btrpc.RunSummary, len(clearedRuns)) + for i := range clearedRuns { + clearedResponse[i] = convertSummary(clearedRuns[i]) + } + remainingResponse := make([]*btrpc.RunSummary, len(remainingRuns)) + for i := range remainingRuns { + remainingResponse[i] = convertSummary(remainingRuns[i]) + } + return &btrpc.ClearAllRunsResponse{ + ClearedRuns: clearedResponse, + RemainingRuns: remainingResponse, }, nil } diff --git a/backtester/engine/grpcserver_test.go b/backtester/engine/grpcserver_test.go index 5b415ba14ab..5b5a77077a4 100644 --- a/backtester/engine/grpcserver_test.go +++ b/backtester/engine/grpcserver_test.go @@ -11,6 +11,11 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/btrpc" "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/config" + "github.com/thrasher-corp/gocryptotrader/backtester/data" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/ftxcashandcarry" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -20,6 +25,22 @@ func TestExecuteStrategyFromFile(t *testing.T) { t.Parallel() s := &GRPCServer{} _, err := s.ExecuteStrategyFromFile(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.config, err = config.GenerateDefaultConfig() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + s.config.Report.GenerateReport = false + _, err = s.ExecuteStrategyFromFile(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.manager = SetupRunManager() + _, err = s.ExecuteStrategyFromFile(context.Background(), nil) if !errors.Is(err, common.ErrNilArguments) { t.Errorf("received '%v' expecting '%v'", err, common.ErrNilArguments) } @@ -32,16 +53,17 @@ func TestExecuteStrategyFromFile(t *testing.T) { _, err = s.ExecuteStrategyFromFile(context.Background(), &btrpc.ExecuteStrategyFromFileRequest{ StrategyFilePath: dcaConfigPath, }) - if !errors.Is(err, common.ErrNilArguments) { - t.Errorf("received '%v' expecting '%v'", err, common.ErrNilArguments) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) } - s.BacktesterConfig = &config.BacktesterConfig{} _, err = s.ExecuteStrategyFromFile(context.Background(), &btrpc.ExecuteStrategyFromFileRequest{ - StrategyFilePath: dcaConfigPath, + StrategyFilePath: dcaConfigPath, + DoNotRunImmediately: true, + DoNotStore: true, }) - if !errors.Is(err, nil) { - t.Errorf("received '%v' expecting '%v'", err, nil) + if !errors.Is(err, errCannotHandleRequest) { + t.Errorf("received '%v' expecting '%v'", err, errCannotHandleRequest) } } @@ -49,12 +71,22 @@ func TestExecuteStrategyFromConfig(t *testing.T) { t.Parallel() s := &GRPCServer{} _, err := s.ExecuteStrategyFromConfig(context.Background(), nil) - if !errors.Is(err, common.ErrNilArguments) { - t.Errorf("received '%v' expecting '%v'", err, common.ErrNilArguments) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) } - s.BacktesterConfig = &config.BacktesterConfig{} - _, err = s.ExecuteStrategyFromConfig(context.Background(), &btrpc.ExecuteStrategyFromConfigRequest{}) + s.config, err = config.GenerateDefaultConfig() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + _, err = s.ExecuteStrategyFromConfig(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.config.Report.GenerateReport = false + s.manager = SetupRunManager() + _, err = s.ExecuteStrategyFromConfig(context.Background(), nil) if !errors.Is(err, common.ErrNilArguments) { t.Errorf("received '%v' expecting '%v'", err, common.ErrNilArguments) } @@ -239,6 +271,15 @@ func TestExecuteStrategyFromConfig(t *testing.T) { t.Errorf("received '%v' expecting '%v'", err, nil) } + _, err = s.ExecuteStrategyFromConfig(context.Background(), &btrpc.ExecuteStrategyFromConfigRequest{ + DoNotRunImmediately: true, + DoNotStore: true, + Config: cfg, + }) + if !errors.Is(err, errCannotHandleRequest) { + t.Errorf("received '%v' expecting '%v'", err, errCannotHandleRequest) + } + // coverage test to ensure the rest of the config can successfully be converted // this will not have a successful response cfg.FundingSettings.UseExchangeLevelFunding = true @@ -283,3 +324,279 @@ func TestExecuteStrategyFromConfig(t *testing.T) { t.Error("expected an error from a bad setup") } } + +func TestListAllRuns(t *testing.T) { + t.Parallel() + s := &GRPCServer{} + _, err := s.ListAllRuns(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.manager = SetupRunManager() + _, err = s.ListAllRuns(context.Background(), nil) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = s.manager.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + resp, err := s.ListAllRuns(context.Background(), &btrpc.ListAllRunsRequest{}) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + if len(resp.Runs) != 1 { + t.Errorf("received '%v' expecting '%v'", len(resp.Runs), 1) + } +} + +func TestGRPCStopRun(t *testing.T) { + t.Parallel() + s := &GRPCServer{} + _, err := s.StopRun(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.manager = SetupRunManager() + _, err = s.StopRun(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = s.manager.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + _, err = s.StopRun(context.Background(), &btrpc.StopRunRequest{ + Id: bt.MetaData.ID.String(), + }) + if !errors.Is(err, errRunHasNotRan) { + t.Errorf("received '%v' expecting '%v'", err, errRunHasNotRan) + } + if len(s.manager.runs) != 1 { + t.Fatalf("received '%v' expecting '%v'", len(s.manager.runs), 1) + } + + s.manager.runs[0].MetaData.DateStarted = time.Now() + _, err = s.StopRun(context.Background(), &btrpc.StopRunRequest{ + Id: bt.MetaData.ID.String(), + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + if s.manager.runs[0].MetaData.DateEnded.IsZero() { + t.Errorf("received '%v' expecting '%v'", s.manager.runs[0].MetaData.DateEnded, "a date") + } +} + +func TestGRPCStopAllRuns(t *testing.T) { + t.Parallel() + s := &GRPCServer{} + _, err := s.StopAllRuns(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.manager = SetupRunManager() + _, err = s.StopAllRuns(context.Background(), nil) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = s.manager.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + resp, err := s.StopAllRuns(context.Background(), &btrpc.StopAllRunsRequest{}) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + if len(s.manager.runs) != 1 { + t.Fatalf("received '%v' expecting '%v'", len(s.manager.runs), 1) + } + if len(resp.RunsStopped) != 0 { + t.Errorf("received '%v' expecting '%v'", len(resp.RunsStopped), 0) + } + + s.manager.runs[0].MetaData.DateStarted = time.Now() + resp, err = s.StopAllRuns(context.Background(), &btrpc.StopAllRunsRequest{}) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + if s.manager.runs[0].MetaData.DateEnded.IsZero() { + t.Errorf("received '%v' expecting '%v'", s.manager.runs[0].MetaData.DateEnded, "a date") + } + if len(resp.RunsStopped) != 1 { + t.Errorf("received '%v' expecting '%v'", len(resp.RunsStopped), 1) + } +} + +func TestGRPCStartRun(t *testing.T) { + t.Parallel() + s := &GRPCServer{} + _, err := s.StartRun(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.manager = SetupRunManager() + _, err = s.StartRun(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = s.manager.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + _, err = s.StartRun(context.Background(), &btrpc.StartRunRequest{ + Id: bt.MetaData.ID.String(), + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + if len(s.manager.runs) != 1 { + t.Fatalf("received '%v' expecting '%v'", len(s.manager.runs), 1) + } + if s.manager.runs[0].MetaData.DateStarted.IsZero() { + t.Errorf("received '%v' expecting '%v'", s.manager.runs[0].MetaData.DateStarted, "a date") + } +} + +func TestGRPCStartAllRuns(t *testing.T) { + t.Parallel() + s := &GRPCServer{} + _, err := s.StartAllRuns(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.manager = SetupRunManager() + _, err = s.StartAllRuns(context.Background(), nil) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = s.manager.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + _, err = s.StartAllRuns(context.Background(), &btrpc.StartAllRunsRequest{}) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + if len(s.manager.runs) != 1 { + t.Fatalf("received '%v' expecting '%v'", len(s.manager.runs), 1) + } + if s.manager.runs[0].MetaData.DateStarted.IsZero() { + t.Errorf("received '%v' expecting '%v'", s.manager.runs[0].MetaData.DateStarted, "a date") + } +} + +func TestGRPCClearRun(t *testing.T) { + t.Parallel() + s := &GRPCServer{} + _, err := s.ClearRun(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.manager = SetupRunManager() + _, err = s.ClearRun(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = s.manager.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + _, err = s.ClearRun(context.Background(), &btrpc.ClearRunRequest{ + Id: bt.MetaData.ID.String(), + }) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + if len(s.manager.runs) != 0 { + t.Fatalf("received '%v' expecting '%v'", len(s.manager.runs), 0) + } +} + +func TestGRPCClearAllRuns(t *testing.T) { + t.Parallel() + s := &GRPCServer{} + _, err := s.ClearAllRuns(context.Background(), nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expecting '%v'", err, gctcommon.ErrNilPointer) + } + + s.manager = SetupRunManager() + _, err = s.ClearAllRuns(context.Background(), nil) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = s.manager.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + _, err = s.ClearAllRuns(context.Background(), &btrpc.ClearAllRunsRequest{}) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expecting '%v'", err, nil) + } + if len(s.manager.runs) != 0 { + t.Fatalf("received '%v' expecting '%v'", len(s.manager.runs), 0) + } +} diff --git a/backtester/engine/runmanager.go b/backtester/engine/runmanager.go new file mode 100644 index 00000000000..0020c229b8b --- /dev/null +++ b/backtester/engine/runmanager.go @@ -0,0 +1,214 @@ +package engine + +import ( + "errors" + "fmt" + + "github.com/gofrs/uuid" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" +) + +var ( + errRunNotFound = errors.New("run not found") + errRunAlreadyMonitored = errors.New("run already monitored") + errAlreadyRan = errors.New("run already ran") + errRunHasNotRan = errors.New("run hasn't ran yet") + errRunIsRunning = errors.New("run is already running") + errCannotClear = errors.New("cannot clear run") +) + +// SetupRunManager creates a run manager to allow the backtester to manage multiple strategies +func SetupRunManager() *RunManager { + return &RunManager{} +} + +// AddRun adds a run to the manager +func (r *RunManager) AddRun(b *BackTest) error { + if r == nil { + return fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + if b == nil { + return fmt.Errorf("%w BackTest", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + for i := range r.runs { + if r.runs[i].Equal(b) { + return fmt.Errorf("%w %s %s", errRunAlreadyMonitored, b.MetaData.ID, b.MetaData.Strategy) + } + } + + err := b.SetupMetaData() + if err != nil { + return err + } + r.runs = append(r.runs, b) + return nil +} + +// List details all backtesting/livestrategy runs +func (r *RunManager) List() ([]*RunSummary, error) { + if r == nil { + return nil, fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + resp := make([]*RunSummary, len(r.runs)) + for i := range r.runs { + sum, err := r.runs[i].GenerateSummary() + if err != nil { + return nil, err + } + resp[i] = sum + } + return resp, nil +} + +// GetSummary returns details about a completed backtesting/livestrategy run +func (r *RunManager) GetSummary(id uuid.UUID) (*RunSummary, error) { + if r == nil { + return nil, fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + for i := range r.runs { + if !r.runs[i].MatchesID(id) { + continue + } + return r.runs[i].GenerateSummary() + } + return nil, fmt.Errorf("%s %w", id, errRunNotFound) +} + +// StopRun stops a backtesting/livestrategy run if enabled, this will run CloseAllPositions +func (r *RunManager) StopRun(id uuid.UUID) error { + if r == nil { + return fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + for i := range r.runs { + switch { + case !r.runs[i].MatchesID(id): + continue + case r.runs[i].IsRunning(): + r.runs[i].Stop() + return nil + case r.runs[i].HasRan(): + return fmt.Errorf("%w %v", errAlreadyRan, id) + default: + return fmt.Errorf("%w %v", errRunHasNotRan, id) + } + } + return fmt.Errorf("%s %w", id, errRunNotFound) +} + +// StopAllRuns stops all running strategies +func (r *RunManager) StopAllRuns() ([]*RunSummary, error) { + if r == nil { + return nil, fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + resp := make([]*RunSummary, 0, len(r.runs)) + for i := range r.runs { + if !r.runs[i].IsRunning() { + continue + } + r.runs[i].Stop() + sum, err := r.runs[i].GenerateSummary() + if err != nil { + return nil, err + } + resp = append(resp, sum) + } + return resp, nil +} + +// StartRun executes a strategy if found +func (r *RunManager) StartRun(id uuid.UUID) error { + if r == nil { + return fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + for i := range r.runs { + switch { + case !r.runs[i].MatchesID(id): + continue + case r.runs[i].IsRunning(): + return fmt.Errorf("%w %v", errRunIsRunning, id) + case r.runs[i].HasRan(): + return fmt.Errorf("%w %v", errAlreadyRan, id) + default: + return r.runs[i].ExecuteStrategy(false) + } + } + return fmt.Errorf("%s %w", id, errRunNotFound) +} + +// StartAllRuns executes all strategies +func (r *RunManager) StartAllRuns() ([]uuid.UUID, error) { + if r == nil { + return nil, fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + executedRuns := make([]uuid.UUID, 0, len(r.runs)) + for i := range r.runs { + if r.runs[i].HasRan() { + continue + } + executedRuns = append(executedRuns, r.runs[i].MetaData.ID) + err := r.runs[i].ExecuteStrategy(false) + if err != nil { + return nil, err + } + } + + return executedRuns, nil +} + +// ClearRun removes a run from memory +func (r *RunManager) ClearRun(id uuid.UUID) error { + if r == nil { + return fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + for i := range r.runs { + if !r.runs[i].MatchesID(id) { + continue + } + if r.runs[i].IsRunning() { + return fmt.Errorf("%w %v, currently running. Stop it first", errCannotClear, r.runs[i].MetaData.ID) + } + r.runs = append(r.runs[:i], r.runs[i+1:]...) + return nil + } + return fmt.Errorf("%s %w", id, errRunNotFound) +} + +// ClearAllRuns removes all runs from memory +func (r *RunManager) ClearAllRuns() (clearedRuns, remainingRuns []*RunSummary, err error) { + if r == nil { + return nil, nil, fmt.Errorf("%w RunManager", gctcommon.ErrNilPointer) + } + r.m.Lock() + defer r.m.Unlock() + for i := 0; i < len(r.runs); i++ { + var run *RunSummary + run, err = r.runs[i].GenerateSummary() + if err != nil { + return nil, nil, err + } + if r.runs[i].IsRunning() { + remainingRuns = append(remainingRuns, run) + } else { + clearedRuns = append(clearedRuns, run) + r.runs = append(r.runs[:i], r.runs[i+1:]...) + i-- + } + } + return clearedRuns, remainingRuns, nil +} diff --git a/backtester/engine/runmanager_test.go b/backtester/engine/runmanager_test.go new file mode 100644 index 00000000000..44ebe33f990 --- /dev/null +++ b/backtester/engine/runmanager_test.go @@ -0,0 +1,425 @@ +package engine + +import ( + "errors" + "testing" + "time" + + "github.com/gofrs/uuid" + "github.com/thrasher-corp/gocryptotrader/backtester/data" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/eventholder" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/ftxcashandcarry" + gctcommon "github.com/thrasher-corp/gocryptotrader/common" +) + +func TestSetupRunManager(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + if rm == nil { + t.Errorf("received '%v' expected '%v'", rm, "&RunManager{}") + } +} + +func TestAddRun(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + err := rm.AddRun(nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } + + bt := &BackTest{} + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if bt.MetaData.ID.IsNil() { + t.Errorf("received '%v' expected '%v'", bt.MetaData.ID, "a random ID") + } + if len(rm.runs) != 1 { + t.Errorf("received '%v' expected '%v'", len(rm.runs), 1) + } + + err = rm.AddRun(bt) + if !errors.Is(err, errRunAlreadyMonitored) { + t.Errorf("received '%v' expected '%v'", err, errRunAlreadyMonitored) + } + if len(rm.runs) != 1 { + t.Errorf("received '%v' expected '%v'", len(rm.runs), 1) + } + + rm = nil + err = rm.AddRun(bt) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestGetSummary(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + id, err := uuid.NewV4() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + _, err = rm.GetSummary(id) + if !errors.Is(err, errRunNotFound) { + t.Errorf("received '%v' expected '%v'", err, errRunNotFound) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + Statistic: &statistics.Statistic{}, + } + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + sum, err := rm.GetSummary(bt.MetaData.ID) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if sum.MetaData.ID != bt.MetaData.ID { + t.Errorf("received '%v' expected '%v'", sum.MetaData.ID, bt.MetaData.ID) + } + + rm = nil + _, err = rm.GetSummary(id) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestList(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + list, err := rm.List() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(list) != 0 { + t.Errorf("received '%v' expected '%v'", len(list), 0) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + Statistic: &statistics.Statistic{}, + } + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + list, err = rm.List() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(list) != 1 { + t.Errorf("received '%v' expected '%v'", len(list), 1) + } + + rm = nil + _, err = rm.List() + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestStopRun(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + list, err := rm.List() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(list) != 0 { + t.Errorf("received '%v' expected '%v'", len(list), 0) + } + + id, err := uuid.NewV4() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = rm.StopRun(id) + if !errors.Is(err, errRunNotFound) { + t.Errorf("received '%v' expected '%v'", err, errRunNotFound) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = rm.StopRun(bt.MetaData.ID) + if !errors.Is(err, errRunHasNotRan) { + t.Errorf("received '%v' expected '%v'", err, errRunHasNotRan) + } + + bt.MetaData.DateStarted = time.Now() + err = rm.StopRun(bt.MetaData.ID) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + err = rm.StopRun(bt.MetaData.ID) + if !errors.Is(err, errAlreadyRan) { + t.Errorf("received '%v' expected '%v'", err, errAlreadyRan) + } + + rm = nil + err = rm.StopRun(id) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestStopAllRuns(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + stoppedRuns, err := rm.StopAllRuns() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(stoppedRuns) != 0 { + t.Errorf("received '%v' expected '%v'", len(stoppedRuns), 0) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + bt.MetaData.DateStarted = time.Now() + stoppedRuns, err = rm.StopAllRuns() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(stoppedRuns) != 1 { + t.Errorf("received '%v' expected '%v'", len(stoppedRuns), 1) + } + + rm = nil + _, err = rm.StopAllRuns() + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestStartRun(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + list, err := rm.List() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(list) != 0 { + t.Errorf("received '%v' expected '%v'", len(list), 0) + } + + id, err := uuid.NewV4() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = rm.StartRun(id) + if !errors.Is(err, errRunNotFound) { + t.Errorf("received '%v' expected '%v'", err, errRunNotFound) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = rm.StartRun(bt.MetaData.ID) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + err = rm.StartRun(bt.MetaData.ID) + if !errors.Is(err, errRunIsRunning) { + t.Errorf("received '%v' expected '%v'", err, errRunIsRunning) + } + + bt.MetaData.DateEnded = time.Now() + bt.MetaData.Closed = true + + err = rm.StartRun(bt.MetaData.ID) + if !errors.Is(err, errAlreadyRan) { + t.Errorf("received '%v' expected '%v'", err, errAlreadyRan) + } + + rm = nil + err = rm.StartRun(id) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestStartAllRuns(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + startedRuns, err := rm.StartAllRuns() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(startedRuns) != 0 { + t.Errorf("received '%v' expected '%v'", len(startedRuns), 0) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + startedRuns, err = rm.StartAllRuns() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(startedRuns) != 1 { + t.Errorf("received '%v' expected '%v'", len(startedRuns), 1) + } + + rm = nil + _, err = rm.StartAllRuns() + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestClearRun(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + + id, err := uuid.NewV4() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + err = rm.ClearRun(id) + if !errors.Is(err, errRunNotFound) { + t.Errorf("received '%v' expected '%v'", err, errRunNotFound) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + bt.MetaData.DateStarted = time.Now() + err = rm.ClearRun(bt.MetaData.ID) + if !errors.Is(err, errCannotClear) { + t.Errorf("received '%v' expected '%v'", err, errCannotClear) + } + + bt.MetaData.DateStarted = time.Time{} + err = rm.ClearRun(bt.MetaData.ID) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + list, err := rm.List() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(list) != 0 { + t.Errorf("received '%v' expected '%v'", len(list), 0) + } + + rm = nil + err = rm.ClearRun(id) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} + +func TestClearAllRuns(t *testing.T) { + t.Parallel() + rm := SetupRunManager() + + clearedRuns, remainingRuns, err := rm.ClearAllRuns() + if len(clearedRuns) != 0 { + t.Errorf("received '%v' expected '%v'", len(clearedRuns), 0) + } + if len(remainingRuns) != 0 { + t.Errorf("received '%v' expected '%v'", len(remainingRuns), 0) + } + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + bt := &BackTest{ + Strategy: &ftxcashandcarry.Strategy{}, + EventQueue: &eventholder.Holder{}, + Datas: &data.HandlerPerCurrency{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), + } + err = rm.AddRun(bt) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + bt.MetaData.DateStarted = time.Now() + clearedRuns, remainingRuns, err = rm.ClearAllRuns() + if len(clearedRuns) != 0 { + t.Errorf("received '%v' expected '%v'", len(clearedRuns), 0) + } + if len(remainingRuns) != 1 { + t.Errorf("received '%v' expected '%v'", len(remainingRuns), 1) + } + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + bt.MetaData.DateStarted = time.Time{} + clearedRuns, remainingRuns, err = rm.ClearAllRuns() + if len(clearedRuns) != 1 { + t.Errorf("received '%v' expected '%v'", len(clearedRuns), 1) + } + if len(remainingRuns) != 0 { + t.Errorf("received '%v' expected '%v'", len(remainingRuns), 0) + } + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + list, err := rm.List() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if len(list) != 0 { + t.Errorf("received '%v' expected '%v'", len(list), 0) + } + + rm = nil + _, _, err = rm.ClearAllRuns() + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } +} diff --git a/backtester/engine/setup.go b/backtester/engine/setup.go index dcb891d1181..ae809a9b3ae 100644 --- a/backtester/engine/setup.go +++ b/backtester/engine/setup.go @@ -40,7 +40,6 @@ import ( gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline" gctorder "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/log" - "github.com/thrasher-corp/gocryptotrader/signaler" ) // NewFromConfig takes a strategy config and configures a backtester variable to run @@ -49,13 +48,16 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool if cfg == nil { return nil, errNilConfig } - var err error - bt := New() + bt, err := New() + if err != nil { + return nil, err + } bt.exchangeManager = engine.SetupExchangeManager() bt.orderManager, err = engine.SetupOrderManager(bt.exchangeManager, &engine.CommunicationManager{}, &sync.WaitGroup{}, false, false, 0) if err != nil { return nil, err } + err = bt.orderManager.Start() if err != nil { return nil, err @@ -348,7 +350,9 @@ func NewFromConfig(cfg *config.Config, templatePath, output string, verbose bool if err != nil { return nil, err } + bt.MetaData.Strategy = bt.Strategy.Name() bt.Strategy.SetDefaults() + if cfg.StrategySettings.CustomSettings != nil { err = bt.Strategy.SetCustomSettings(cfg.StrategySettings.CustomSettings) if err != nil && !errors.Is(err, base.ErrCustomSettingsUnsupported) { @@ -514,6 +518,8 @@ func (bt *BackTest) setupExchangeSettings(cfg *config.Config) (exchange.Exchange realOrders := false if cfg.DataSettings.LiveData != nil { realOrders = cfg.DataSettings.LiveData.RealOrders + bt.MetaData.LiveTesting = true + bt.MetaData.RealOrders = realOrders } buyRule := exchange.MinMax{ @@ -880,45 +886,24 @@ func loadLiveData(cfg *config.Config, base *gctexchange.Base) error { return nil } -// ExecuteStrategy executes the strategy using the provided configs -func ExecuteStrategy(strategyCfg *config.Config, backtesterCfg *config.BacktesterConfig) error { - if err := strategyCfg.Validate(); err != nil { - return err +// NewBacktesterFromConfigs creates a new backtester based on config settings +func NewBacktesterFromConfigs(strategyCfg *config.Config, backtesterCfg *config.BacktesterConfig) (*BackTest, error) { + if strategyCfg == nil { + return nil, fmt.Errorf("%w strategy config", gctcommon.ErrNilPointer) } if backtesterCfg == nil { - err := fmt.Errorf("%w backtester config", common.ErrNilArguments) - return err + return nil, fmt.Errorf("%w backtester config", gctcommon.ErrNilPointer) + } + if err := strategyCfg.Validate(); err != nil { + return nil, err } bt, err := NewFromConfig(strategyCfg, backtesterCfg.Report.TemplatePath, backtesterCfg.Report.OutputPath, backtesterCfg.Verbose) if err != nil { - return err - } - if strategyCfg.DataSettings.LiveData != nil { - go func() { - err = bt.RunLive() - if err != nil { - log.Error(log.Global, err) - return - } - }() - interrupt := signaler.WaitForInterrupt() - log.Infof(log.Global, "Captured %v, shutdown requested.\n", interrupt) - bt.Stop() - } else { - bt.Run() + return nil, err } - - err = bt.Statistic.CalculateAllResults() + err = bt.SetupMetaData() if err != nil { - return err - } - if backtesterCfg.Report.GenerateReport { - bt.Reports.UseDarkMode(backtesterCfg.Report.DarkMode) - err = bt.Reports.GenerateReport() - if err != nil { - return err - } + return nil, err } - - return nil + return bt, nil } diff --git a/backtester/engine/setup_test.go b/backtester/engine/setup_test.go index a0d13ebed79..a8dc43b6220 100644 --- a/backtester/engine/setup_test.go +++ b/backtester/engine/setup_test.go @@ -2,6 +2,7 @@ package engine import ( "errors" + "path/filepath" "strings" "testing" "time" @@ -10,6 +11,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/backtester/common" "github.com/thrasher-corp/gocryptotrader/backtester/config" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio/holdings" + "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/statistics" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base" "github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/dollarcostaverage" "github.com/thrasher-corp/gocryptotrader/backtester/report" @@ -128,7 +130,8 @@ func TestNewFromConfig(t *testing.T) { func TestLoadDataAPI(t *testing.T) { t.Parallel() bt := BackTest{ - Reports: &report.Data{}, + Reports: &report.Data{}, + Statistic: &statistics.Statistic{}, } cp := currency.NewPair(currency.BTC, currency.USDT) cfg := &config.Config{ @@ -185,7 +188,8 @@ func TestLoadDataAPI(t *testing.T) { func TestLoadDataDatabase(t *testing.T) { t.Parallel() bt := BackTest{ - Reports: &report.Data{}, + Reports: &report.Data{}, + Statistic: &statistics.Statistic{}, } cp := currency.NewPair(currency.BTC, currency.USDT) cfg := &config.Config{ @@ -253,7 +257,8 @@ func TestLoadDataDatabase(t *testing.T) { func TestLoadDataCSV(t *testing.T) { t.Parallel() bt := BackTest{ - Reports: &report.Data{}, + Reports: &report.Data{}, + Statistic: &statistics.Statistic{}, } cp := currency.NewPair(currency.BTC, currency.USDT) cfg := &config.Config{ @@ -310,8 +315,9 @@ func TestLoadDataCSV(t *testing.T) { func TestLoadDataLive(t *testing.T) { t.Parallel() bt := BackTest{ - Reports: &report.Data{}, - shutdown: make(chan struct{}), + Reports: &report.Data{}, + Statistic: &statistics.Statistic{}, + shutdown: make(chan struct{}), } cp := currency.NewPair(currency.BTC, currency.USDT) cfg := &config.Config{ @@ -367,3 +373,39 @@ func TestLoadDataLive(t *testing.T) { } bt.Stop() } + +func TestNewBacktesterFromConfigs(t *testing.T) { + t.Parallel() + _, err := NewBacktesterFromConfigs(nil, nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } + + strat1 := filepath.Join("..", "config", "strategyexamples", "dca-api-candles.strat") + cfg, err := config.ReadStrategyConfigFromFile(strat1) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + dc, err := config.GenerateDefaultConfig() + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + + _, err = NewBacktesterFromConfigs(cfg, nil) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } + + _, err = NewBacktesterFromConfigs(nil, dc) + if !errors.Is(err, gctcommon.ErrNilPointer) { + t.Errorf("received '%v' expected '%v'", err, gctcommon.ErrNilPointer) + } + + bt, err := NewBacktesterFromConfigs(cfg, dc) + if !errors.Is(err, nil) { + t.Errorf("received '%v' expected '%v'", err, nil) + } + if bt.MetaData.DateLoaded.IsZero() { + t.Errorf("received '%v' expected '%v'", bt.MetaData.DateLoaded, "a date") + } +} diff --git a/backtester/eventhandlers/statistics/statistics.go b/backtester/eventhandlers/statistics/statistics.go index fd766eadf20..5b74cb02abf 100644 --- a/backtester/eventhandlers/statistics/statistics.go +++ b/backtester/eventhandlers/statistics/statistics.go @@ -297,6 +297,15 @@ func (s *Statistic) SetStrategyName(name string) { // Serialise outputs the Statistic struct in json func (s *Statistic) Serialise() (string, error) { + s.CurrencyStatistics = nil + for _, exchangeMap := range s.ExchangeAssetPairStatistics { + for _, assetMap := range exchangeMap { + for _, stats := range assetMap { + s.CurrencyStatistics = append(s.CurrencyStatistics, stats) + } + } + } + resp, err := json.MarshalIndent(s, "", " ") if err != nil { return "", err diff --git a/backtester/eventhandlers/statistics/statistics_types.go b/backtester/eventhandlers/statistics/statistics_types.go index b72e9ebb0ce..1003fbde2e5 100644 --- a/backtester/eventhandlers/statistics/statistics_types.go +++ b/backtester/eventhandlers/statistics/statistics_types.go @@ -41,7 +41,8 @@ type Statistic struct { EndDate time.Time `json:"end-date"` CandleInterval gctkline.Interval `json:"candle-interval"` RiskFreeRate decimal.Decimal `json:"risk-free-rate"` - ExchangeAssetPairStatistics map[string]map[asset.Item]map[currency.Pair]*CurrencyPairStatistic `json:"exchange-asset-pair-statistics"` + ExchangeAssetPairStatistics map[string]map[asset.Item]map[currency.Pair]*CurrencyPairStatistic `json:"-"` + CurrencyStatistics []*CurrencyPairStatistic `json:"currency-statistics"` TotalBuyOrders int64 `json:"total-buy-orders"` TotalLongOrders int64 `json:"total-long-orders"` TotalShortOrders int64 `json:"total-short-orders"` @@ -164,11 +165,11 @@ type CurrencyPairStatistic struct { UnrealisedPNL decimal.Decimal `json:"unrealised-pnl"` RealisedPNL decimal.Decimal `json:"realised-pnl"` CompoundAnnualGrowthRate decimal.Decimal `json:"compound-annual-growth-rate"` - TotalAssetValue decimal.Decimal - TotalFees decimal.Decimal - TotalValueLostToVolumeSizing decimal.Decimal - TotalValueLostToSlippage decimal.Decimal - TotalValueLost decimal.Decimal + TotalAssetValue decimal.Decimal `json:"total-asset-value"` + TotalFees decimal.Decimal `json:"total-fees"` + TotalValueLostToVolumeSizing decimal.Decimal `json:"total-value-lost-to-volume-sizing"` + TotalValueLostToSlippage decimal.Decimal `json:"total-value-lost-to-slippage"` + TotalValueLost decimal.Decimal `json:"total-value-lost"` Events []DataAtOffset `json:"-"` @@ -194,7 +195,7 @@ type Swing struct { Highest ValueAtTime `json:"highest"` Lowest ValueAtTime `json:"lowest"` DrawdownPercent decimal.Decimal `json:"drawdown"` - IntervalDuration int64 + IntervalDuration int64 `json:"interval-duration"` } // ValueAtTime is an individual iteration of price at a time @@ -211,55 +212,55 @@ type relatedCurrencyPairStatistics struct { // FundingStatistics stores all funding related statistics type FundingStatistics struct { - Report *funding.Report - Items []FundingItemStatistics - TotalUSDStatistics *TotalFundingStatistics + Report *funding.Report `json:"-"` + Items []FundingItemStatistics `json:"funding-item-statistics"` + TotalUSDStatistics *TotalFundingStatistics `json:"total-usd-statistics"` } // FundingItemStatistics holds statistics for funding items type FundingItemStatistics struct { - ReportItem *funding.ReportItem + ReportItem *funding.ReportItem `json:"-"` // USD stats - StartingClosePrice ValueAtTime - EndingClosePrice ValueAtTime - LowestClosePrice ValueAtTime - HighestClosePrice ValueAtTime - MarketMovement decimal.Decimal - StrategyMovement decimal.Decimal - DidStrategyBeatTheMarket bool - RiskFreeRate decimal.Decimal - CompoundAnnualGrowthRate decimal.Decimal - BuyOrders int64 - SellOrders int64 - TotalOrders int64 - MaxDrawdown Swing - HighestCommittedFunds ValueAtTime + StartingClosePrice ValueAtTime `json:"starting-close-price"` + EndingClosePrice ValueAtTime `json:"ending-close-price"` + LowestClosePrice ValueAtTime `json:"lowest-close-price"` + HighestClosePrice ValueAtTime `json:"highest-close-price"` + MarketMovement decimal.Decimal `json:"market-movement"` + StrategyMovement decimal.Decimal `json:"strategy-movement"` + DidStrategyBeatTheMarket bool `json:"did-strategy-beat-the-market"` + RiskFreeRate decimal.Decimal `json:"risk-free-rate"` + CompoundAnnualGrowthRate decimal.Decimal `json:"compound-annual-growth-rate"` + BuyOrders int64 `json:"buy-orders"` + SellOrders int64 `json:"sell-orders"` + TotalOrders int64 `json:"total-orders"` + MaxDrawdown Swing `json:"max-drawdown"` + HighestCommittedFunds ValueAtTime `json:"highest-committed-funds"` // CollateralPair stats - IsCollateral bool - InitialCollateral ValueAtTime - FinalCollateral ValueAtTime - HighestCollateral ValueAtTime - LowestCollateral ValueAtTime + IsCollateral bool `json:"is-collateral"` + InitialCollateral ValueAtTime `json:"initial-collateral"` + FinalCollateral ValueAtTime `json:"final-collateral"` + HighestCollateral ValueAtTime `json:"highest-collateral"` + LowestCollateral ValueAtTime `json:"lowest-collateral"` // Contracts - LowestHoldings ValueAtTime - HighestHoldings ValueAtTime - InitialHoldings ValueAtTime - FinalHoldings ValueAtTime + LowestHoldings ValueAtTime `json:"lowest-holdings"` + HighestHoldings ValueAtTime `json:"highest-holdings"` + InitialHoldings ValueAtTime `json:"initial-holdings"` + FinalHoldings ValueAtTime `json:"final-holdings"` } // TotalFundingStatistics holds values for overall statistics for funding items type TotalFundingStatistics struct { - HoldingValues []ValueAtTime - HighestHoldingValue ValueAtTime - LowestHoldingValue ValueAtTime - BenchmarkMarketMovement decimal.Decimal - StrategyMovement decimal.Decimal - RiskFreeRate decimal.Decimal - CompoundAnnualGrowthRate decimal.Decimal - MaxDrawdown Swing - GeometricRatios *Ratios - ArithmeticRatios *Ratios - DidStrategyBeatTheMarket bool - DidStrategyMakeProfit bool - HoldingValueDifference decimal.Decimal + HoldingValues []ValueAtTime `json:"-"` + HighestHoldingValue ValueAtTime `json:"highest-holding-value"` + LowestHoldingValue ValueAtTime `json:"lowest-holding-value"` + BenchmarkMarketMovement decimal.Decimal `json:"benchmark-market-movement"` + StrategyMovement decimal.Decimal `json:"strategy-movement"` + RiskFreeRate decimal.Decimal `json:"risk-free-rate"` + CompoundAnnualGrowthRate decimal.Decimal `json:"compound-annual-growth-rate"` + MaxDrawdown Swing `json:"max-drawdown"` + GeometricRatios *Ratios `json:"geometric-ratios"` + ArithmeticRatios *Ratios `json:"arithmetic-ratios"` + DidStrategyBeatTheMarket bool `json:"did-strategy-beat-the-market"` + DidStrategyMakeProfit bool `json:"did-strategy-make-profit"` + HoldingValueDifference decimal.Decimal `json:"holding-value-difference"` } diff --git a/backtester/main.go b/backtester/main.go index 4522233218c..a6a53bafb7e 100644 --- a/backtester/main.go +++ b/backtester/main.go @@ -152,7 +152,8 @@ func main() { fmt.Printf("Could not read strategy config. Error: %v.\n", err) os.Exit(1) } - err = backtest.ExecuteStrategy(cfg, &config.BacktesterConfig{ + var bt *backtest.BackTest + bt, err = backtest.NewBacktesterFromConfigs(cfg, &config.BacktesterConfig{ Report: config.Report{ GenerateReport: generateReport, TemplatePath: btCfg.Report.TemplatePath, @@ -164,15 +165,36 @@ func main() { fmt.Printf("Could not execute strategy. Error: %v.\n", err) os.Exit(1) } + if bt.MetaData.LiveTesting { + err = bt.ExecuteStrategy(false) + if err != nil { + fmt.Printf("Could execute strategy. Error: %v.\n", err) + os.Exit(1) + } + interrupt := signaler.WaitForInterrupt() + log.Infof(log.Global, "Captured %v, shutdown requested.\n", interrupt) + log.Infoln(log.Global, "Exiting.") + bt.Stop() + } else { + err = bt.ExecuteStrategy(true) + if err != nil { + fmt.Printf("Could execute strategy. Error: %v.\n", err) + os.Exit(1) + } + } return } + // grpc server mode btCfg.Report.DarkMode = darkReport btCfg.Report.GenerateReport = generateReport + runManager := backtest.SetupRunManager() + go func(c *config.BacktesterConfig) { log.Info(log.GRPCSys, "Starting RPC server") - s := backtest.SetupRPCServer(c) + var s *backtest.GRPCServer + s, err = backtest.SetupRPCServer(c, runManager) err = backtest.StartRPCServer(s) if err != nil { fmt.Printf("Could not start RPC server. Error: %v.\n", err) diff --git a/backtester/report/report.go b/backtester/report/report.go index bc8ce62142b..eda1d5d58fe 100644 --- a/backtester/report/report.go +++ b/backtester/report/report.go @@ -18,6 +18,9 @@ import ( // GenerateReport sends final data from statistics to a template // to create a lovely final report for someone to view func (d *Data) GenerateReport() error { + if d.TemplatePath == "" || d.OutputPath == "" { + return nil + } log.Info(common.Report, "Generating report") err := d.enhanceCandles() if err != nil { diff --git a/common/timeperiods/timeperiods.go b/common/timeperiods/timeperiods.go index 6e118ff1e26..a13d954a129 100644 --- a/common/timeperiods/timeperiods.go +++ b/common/timeperiods/timeperiods.go @@ -111,7 +111,7 @@ func (t *TimePeriodCalculator) calculatePeriods() { return } iterateDateMate := t.start - for !iterateDateMate.Equal(t.end) && !iterateDateMate.After(t.end) { + for !iterateDateMate.Equal(t.end) && iterateDateMate.Before(t.end) { tp := TimePeriod{ Time: iterateDateMate, dataInRange: false, diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index 6e752eea3e0..6efe2e60760 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -286,7 +286,7 @@ func (b *Binance) batchAggregateTrades(ctx context.Context, arg *AggregatedTrade // cutting off trades for high activity pairs increment := time.Second * 10 for start := arg.StartTime; len(resp) == 0; start = start.Add(increment) { - if !arg.EndTime.IsZero() && !start.Before(arg.EndTime) { + if !arg.EndTime.IsZero() && start.After(arg.EndTime) { // All requests returned empty return nil, nil } diff --git a/exchanges/binanceus/binanceus.go b/exchanges/binanceus/binanceus.go index 634bf1049d5..d73d71aaf19 100644 --- a/exchanges/binanceus/binanceus.go +++ b/exchanges/binanceus/binanceus.go @@ -288,7 +288,7 @@ func (bi *Binanceus) batchAggregateTrades(ctx context.Context, arg *AggregatedTr increment := time.Second * 10 for len(resp) == 0 { startTime = startTime.Add(increment) - if !endTime.IsZero() && !startTime.Before(endTime) { + if !endTime.IsZero() && startTime.After(endTime) { // All requests returned empty return nil, nil } @@ -915,10 +915,10 @@ func (bi *Binanceus) GetSubaccountTransferHistory(ctx context.Context, hundredDayBefore := time.Now() hundredDayBefore.Sub(time.UnixMilli(int64((time.Hour * 24 * 10) / time.Millisecond))) - if !(startTimeT.Before(hundredDayBefore)) || !startTimeT.After(time.Now()) { + if !(startTimeT.Before(hundredDayBefore)) || startTimeT.Before(time.Now()) { params.Set("startTime", strconv.Itoa(int(startTime))) } - if !(endTimeT.Before(hundredDayBefore)) || !endTimeT.After(time.Now()) { + if !(endTimeT.Before(hundredDayBefore)) || endTimeT.Before(time.Now()) { params.Set("startTime", strconv.Itoa(int(endTime))) } return resp.Transfers, bi.SendAuthHTTPRequest(ctx, diff --git a/exchanges/kline/kline.go b/exchanges/kline/kline.go index 9b19840a2aa..0e3c94f2c11 100644 --- a/exchanges/kline/kline.go +++ b/exchanges/kline/kline.go @@ -415,7 +415,7 @@ func CalculateCandleDateRanges(start, end time.Time, interval Interval, limit ui End: CreateIntervalTime(end), } var intervalsInWholePeriod []IntervalData - for i := start; !i.After(end) && !i.Equal(end); i = i.Add(interval.Duration()) { + for i := start; i.Before(end) && !i.Equal(end); i = i.Add(interval.Duration()) { intervalsInWholePeriod = append(intervalsInWholePeriod, IntervalData{ Start: CreateIntervalTime(i.Round(interval.Duration())), End: CreateIntervalTime(i.Round(interval.Duration()).Add(interval.Duration())), diff --git a/log/logger_multiwriter.go b/log/logger_multiwriter.go index 4c7639706ea..aeb90d741dd 100644 --- a/log/logger_multiwriter.go +++ b/log/logger_multiwriter.go @@ -7,8 +7,8 @@ import ( ) var ( - errWriterAlreadyLoaded = errors.New("io.Writer already loaded") errWriterNotFound = errors.New("io.Writer not found") + errWriterAlreadyLoaded = errors.New("io.Writer already loaded") ) // Add appends a new writer to the multiwriter slice