diff --git a/pkg/bbgo/trader.go b/pkg/bbgo/trader.go index b874e1fa8..5a1e8ac21 100644 --- a/pkg/bbgo/trader.go +++ b/pkg/bbgo/trader.go @@ -2,7 +2,6 @@ package bbgo import ( "context" - "time" "github.com/pkg/errors" "github.com/robfig/cron/v3" @@ -96,7 +95,6 @@ func (reporter *AverageCostPnLReporter) Run() { for _, sessionName := range reporter.Sessions { session := reporter.environment.sessions[sessionName] calculator := &pnl.AverageCostCalculator{ - StartTime: time.Time{}, TradingFeeCurrency: session.Exchange.PlatformFeeCurrency(), } diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index 09e62a2bf..dbe99c163 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -24,9 +24,60 @@ func init() { RootCmd.AddCommand(RunCmd) } +func runConfig(ctx context.Context, configFile string) error { + userConfig, err := config.Load(configFile) + if err != nil { + return err + } + + slackToken := viper.GetString("slack-token") + if len(slackToken) == 0 { + return errSlackTokenUndefined + } + + logrus.AddHook(slacklog.NewLogHook(slackToken, viper.GetString("slack-error-channel"))) + + var notifier = slacknotifier.New(slackToken, viper.GetString("slack-channel")) + + db, err := cmdutil.ConnectMySQL() + if err != nil { + return err + } + + environ := bbgo.NewDefaultEnvironment(db) + environ.ReportTrade(notifier) + + trader := bbgo.NewTrader(environ) + + for _, entry := range userConfig.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 { + logrus.Infof("attaching strategy %T", strategy) + trader.AttachCrossExchangeStrategy(strategy) + } + + for _, report := range userConfig.PnLReporters { + if len(report.AverageCostBySymbols) > 0 { + trader.ReportPnL(notifier). + AverageCostBySymbols(report.AverageCostBySymbols...). + Of(report.Of...). + When(report.When...) + } else { + return errors.Errorf("unsupported PnL reporter: %+v", report) + } + } + + return trader.Run(ctx) +} + var RunCmd = &cobra.Command{ Use: "run", - Short: "run strategies", + Short: "run strategies from config file", // SilenceUsage is an option to silence usage when an error occurs. SilenceUsage: true, @@ -38,63 +89,13 @@ var RunCmd = &cobra.Command{ } if len(configFile) == 0 { - return errors.New("--config option is not given") - } - - userConfig, err := config.Load(configFile) - if err != nil { - return err + return errors.New("--config option is required") } ctx, cancel := context.WithCancel(context.Background()) defer cancel() - slackToken := viper.GetString("slack-token") - if len(slackToken) == 0 { - return errSlackTokenUndefined - } - - logrus.AddHook(slacklog.NewLogHook(slackToken, viper.GetString("slack-error-channel"))) - - var notifier = slacknotifier.New(slackToken, viper.GetString("slack-channel")) - - db, err := cmdutil.ConnectMySQL() - if err != nil { - return err - } - - environ := bbgo.NewDefaultEnvironment(db) - environ.ReportTrade(notifier) - - trader := bbgo.NewTrader(environ) - - for _, entry := range userConfig.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 { - logrus.Infof("attaching strategy %T", strategy) - trader.AttachCrossExchangeStrategy(strategy) - } - - for _, report := range userConfig.PnLReporters { - if len(report.AverageCostBySymbols) > 0 { - trader.ReportPnL(notifier). - AverageCostBySymbols(report.AverageCostBySymbols...). - Of(report.Of...). - When(report.When...) - } else { - return errors.Errorf("unsupported PnL reporter: %+v", report) - } - } - - err = trader.Run(ctx) - if err != nil { - return err - } + err = runConfig(ctx, configFile) cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM) return err diff --git a/pkg/config/loader.go b/pkg/config/loader.go index 0c498480e..f1fee61f8 100644 --- a/pkg/config/loader.go +++ b/pkg/config/loader.go @@ -67,28 +67,32 @@ type Config struct { type Stash map[string]interface{} -func loadStash(configFile string) (Stash, error) { - config, err := ioutil.ReadFile(configFile) - if err != nil { - return nil, err - } - +func loadStash(config []byte) (Stash, error) { stash := make(Stash) if err := yaml.Unmarshal(config, stash); err != nil { return nil, err } - return stash, err + return stash, nil } func Load(configFile string) (*Config, error) { var config Config - stash, err := loadStash(configFile) + content, err := ioutil.ReadFile(configFile) if err != nil { return nil, err } + stash, err := loadStash(content) + if err != nil { + return nil, err + } + + if err := loadImports(&config, stash); err != nil { + return nil, err + } + if err := loadExchangeStrategies(&config, stash); err != nil { return nil, err } @@ -104,6 +108,21 @@ func Load(configFile string) (*Config, error) { return &config, nil } +func loadImports(config *Config, stash Stash) error { + importStash, ok := stash["imports"] + if !ok { + return nil + } + + imports, err := reUnmarshal(importStash, &config.Imports) + if err != nil { + return err + } + + config.Imports = *imports.(*[]string) + return nil +} + func loadReportPnL(config *Config, stash Stash) error { reporterStash, ok := stash["reportPnL"] if !ok { @@ -120,16 +139,16 @@ func loadReportPnL(config *Config, stash Stash) error { } func loadCrossExchangeStrategies(config *Config, stash Stash) (err error) { - if len(bbgo.LoadedCrossExchangeStrategies) == 0 { - return errors.New("no cross exchange strategy is registered") - } - - exchangeStrategiesConf, ok := stash["crossExchangeStrategies"] if !ok { return nil } + if len(bbgo.LoadedCrossExchangeStrategies) == 0 { + return errors.New("no cross exchange strategy is registered") + } + + configList, ok := exchangeStrategiesConf.([]interface{}) if !ok { return errors.New("expecting list in crossExchangeStrategies") @@ -158,21 +177,20 @@ func loadCrossExchangeStrategies(config *Config, stash Stash) (err error) { } func loadExchangeStrategies(config *Config, stash Stash) (err error) { - if len(bbgo.LoadedExchangeStrategies) == 0 { - return errors.New("no exchange strategy is registered") - } - exchangeStrategiesConf, ok := stash["exchangeStrategies"] if !ok { return nil } + if len(bbgo.LoadedExchangeStrategies) == 0 { + return errors.New("no exchange strategy is registered") + } + configList, ok := exchangeStrategiesConf.([]interface{}) if !ok { return errors.New("expecting list in exchangeStrategies") } - for _, entry := range configList { configStash, ok := entry.(Stash) if !ok { @@ -188,7 +206,6 @@ func loadExchangeStrategies(config *Config, stash Stash) (err error) { } } - for id, conf := range configStash { // look up the real struct type if st, ok := bbgo.LoadedExchangeStrategies[id]; ok {