From 9ce9ecc9107dcd5540e94c8c66a5813e95d67168 Mon Sep 17 00:00:00 2001 From: c9s Date: Sat, 24 Oct 2020 15:38:13 +0800 Subject: [PATCH] compile local strategies into the wrapper binary --- cmd/bbgo/main.go | 1 - config/bbgo.yaml | 28 +++++++------ pkg/cmd/run.go | 107 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 113 insertions(+), 23 deletions(-) diff --git a/cmd/bbgo/main.go b/cmd/bbgo/main.go index b51585c70..101a330c5 100644 --- a/cmd/bbgo/main.go +++ b/cmd/bbgo/main.go @@ -2,7 +2,6 @@ package main import ( "github.com/c9s/bbgo/pkg/cmd" - ) func main() { diff --git a/config/bbgo.yaml b/config/bbgo.yaml index adc09ce12..7763bcb78 100644 --- a/config/bbgo.yaml +++ b/config/bbgo.yaml @@ -1,4 +1,6 @@ --- +imports: +- github.com/c9s/bbgo/pkg/strategy/buyandhold notifications: slack: defaultChannel: "bbgo" @@ -12,13 +14,13 @@ reportTrades: "sxpusdt": "bbgo-sxpusdt" reportPnL: - - averageCostBySymbols: - - "BTCUSDT" - - "BNBUSDT" - of: binance - when: - - "@daily" - - "@hourly" +- averageCostBySymbols: + - "BTCUSDT" + - "BNBUSDT" + of: binance + when: + - "@daily" + - "@hourly" sessions: max: @@ -31,9 +33,9 @@ sessions: secretVar: BINANCE_API_SECRET exchangeStrategies: - - on: binance - buyandhold: - symbol: "BTCUSDT" - interval: "1m" - baseQuantity: 0.01 - minDropPercentage: -0.02 +- on: binance + buyandhold: + symbol: "BTCUSDT" + interval: "1m" + baseQuantity: 0.01 + minDropPercentage: -0.02 diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index dbe99c163..c361c554b 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -1,12 +1,19 @@ package cmd import ( + "bytes" "context" + "io/ioutil" + "os" + "os/exec" + "path/filepath" "syscall" + "text/template" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + flag "github.com/spf13/pflag" "github.com/spf13/viper" "github.com/c9s/bbgo/pkg/bbgo" @@ -14,22 +21,46 @@ import ( "github.com/c9s/bbgo/pkg/config" "github.com/c9s/bbgo/pkg/notifier/slacknotifier" "github.com/c9s/bbgo/pkg/slack/slacklog" + + // import built-in strategies + _ "github.com/c9s/bbgo/pkg/strategy/buyandhold" ) var errSlackTokenUndefined = errors.New("slack token is not defined.") func init() { + RunCmd.Flags().Bool("no-compile", false, "do not compile wrapper binary") RunCmd.Flags().String("config", "config/bbgo.yaml", "strategy config file") RunCmd.Flags().String("since", "", "pnl since time") RootCmd.AddCommand(RunCmd) } -func runConfig(ctx context.Context, configFile string) error { - userConfig, err := config.Load(configFile) - if err != nil { +var runTemplate = template.Must(template.New("main").Parse(`package main +// DO NOT MODIFY THIS FILE. THIS FILE IS GENERATED FOR IMPORTING STRATEGIES +import ( + "github.com/c9s/bbgo/pkg/cmd" + +{{- range .Imports }} + _ "{{ . }}" +{{- end }} +) + +func main() { + cmd.Execute() +} + +`)) + +func compileRunFile(filepath string, config *config.Config) error { + var buf = bytes.NewBuffer(nil) + if err := runTemplate.Execute(buf, config); err != nil { return err } + return ioutil.WriteFile(filepath, buf.Bytes(), 0644) +} + +func runConfig(ctx context.Context, config *config.Config) error { slackToken := viper.GetString("slack-token") if len(slackToken) == 0 { return errSlackTokenUndefined @@ -49,19 +80,19 @@ func runConfig(ctx context.Context, configFile string) error { trader := bbgo.NewTrader(environ) - for _, entry := range userConfig.ExchangeStrategies { + for _, entry := range config.ExchangeStrategies { for _, mount := range entry.Mounts { logrus.Infof("attaching strategy %T on %s...", entry.Strategy, mount) trader.AttachStrategyOn(mount, entry.Strategy) } } - for _, strategy := range userConfig.CrossExchangeStrategies { + for _, strategy := range config.CrossExchangeStrategies { logrus.Infof("attaching strategy %T", strategy) trader.AttachCrossExchangeStrategy(strategy) } - for _, report := range userConfig.PnLReporters { + for _, report := range config.PnLReporters { if len(report.AverageCostBySymbols) > 0 { trader.ReportPnL(notifier). AverageCostBySymbols(report.AverageCostBySymbols...). @@ -92,12 +123,70 @@ var RunCmd = &cobra.Command{ return errors.New("--config option is required") } + noCompile, err := cmd.Flags().GetBool("no-compile") + if err != nil { + return err + } + ctx, cancel := context.WithCancel(context.Background()) defer cancel() - err = runConfig(ctx, configFile) + userConfig, err := config.Load(configFile) + if err != nil { + return err + } - cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM) - return err + if noCompile { + if err := runConfig(ctx, userConfig); err != nil { + return err + } + cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM) + return nil + } else { + buildDir := filepath.Join("build", "bbgow") + if _, err := os.Stat(buildDir); os.IsNotExist(err) { + if err := os.MkdirAll(buildDir, 0777); err != nil { + return errors.Wrapf(err, "can not create build directory: %s", buildDir) + } + } + + mainFile := filepath.Join(buildDir, "main.go") + if err := compileRunFile(mainFile, userConfig); err != nil { + return errors.Wrap(err, "compile error") + } + + // TODO: use "\" for Windows + cwd, err := os.Getwd() + if err != nil { + return err + } + + buildTarget := filepath.Join(cwd, buildDir) + logrus.Infof("building binary from %s...", buildTarget) + + buildCmd := exec.CommandContext(ctx, "go", "build", "-tags", "wrapper", "-o", "bbgow", buildTarget) + buildCmd.Stdout = os.Stdout + buildCmd.Stderr = os.Stderr + if err := buildCmd.Run(); err != nil { + return err + } + + var flagsArgs = []string{"run", "--no-compile"} + cmd.Flags().Visit(func(flag *flag.Flag) { + flagsArgs = append(flagsArgs, flag.Name, flag.Value.String()) + }) + flagsArgs = append(flagsArgs, args...) + + executePath := filepath.Join(cwd, "bbgow") + runCmd := exec.CommandContext(ctx, executePath, flagsArgs...) + runCmd.Stdout = os.Stdout + runCmd.Stderr = os.Stderr + if err := runCmd.Run(); err != nil { + return err + } + + } + + return nil }, }