diff --git a/pkg/bbgo/trader.go b/pkg/bbgo/trader.go index f4f376eaf..cc4eebb7c 100644 --- a/pkg/bbgo/trader.go +++ b/pkg/bbgo/trader.go @@ -8,13 +8,26 @@ import ( "github.com/jmoiron/sqlx" "github.com/pkg/errors" log "github.com/sirupsen/logrus" + "github.com/spf13/viper" + "github.com/c9s/bbgo/cmd/cmdutil" "github.com/c9s/bbgo/pkg/service" "github.com/c9s/bbgo/pkg/types" _ "github.com/go-sql-driver/mysql" + flag "github.com/spf13/pflag" ) +var SupportedExchanges = []types.ExchangeName{"binance", "max"} + +// PersistentFlags defines the flags for environments +func PersistentFlags(flags *flag.FlagSet) { + flags.String("binance-api-key", "", "binance api key") + flags.String("binance-api-secret", "", "binance api secret") + flags.String("max-api-key", "", "max api key") + flags.String("max-api-secret", "", "max api secret") +} + // SingleExchangeStrategy represents the single Exchange strategy type SingleExchangeStrategy interface { Run(ctx context.Context, orderExecutor types.OrderExecutor, session *ExchangeSession) error @@ -70,6 +83,23 @@ type Environment struct { sessions map[string]*ExchangeSession } +func NewDefaultEnvironment(db *sqlx.DB) *Environment { + environment := NewEnvironment(db) + + for _, n := range SupportedExchanges { + if viper.IsSet(string(n) + "-api-key") { + exchange, err := cmdutil.NewExchange(n) + if err != nil { + panic(err) + } + + environment.AddExchange(string(n), exchange) + } + } + + return environment +} + func NewEnvironment(db *sqlx.DB) *Environment { tradeService := &service.TradeService{DB: db} return &Environment{ @@ -83,12 +113,12 @@ func NewEnvironment(db *sqlx.DB) *Environment { func (environ *Environment) AddExchange(name string, exchange types.Exchange) (session *ExchangeSession) { session = &ExchangeSession{ - Name: name, - Exchange: exchange, + Name: name, + Exchange: exchange, Subscriptions: make(map[types.Subscription]types.Subscription), - Markets: make(map[string]types.Market), - Trades: make(map[string][]types.Trade), - LastPrices: make(map[string]float64), + Markets: make(map[string]types.Market), + Trades: make(map[string][]types.Trade), + LastPrices: make(map[string]float64), } environ.sessions[name] = session @@ -188,9 +218,8 @@ func (environ *Environment) Connect(ctx context.Context) error { return nil } - type Notifiability struct { - notifiers []Notifier + notifiers []Notifier } func (m *Notifiability) AddNotifier(notifier Notifier) { @@ -203,7 +232,6 @@ func (m *Notifiability) Notify(msg string, args ...interface{}) { } } - type Trader struct { Notifiability environment *Environment @@ -263,7 +291,7 @@ func (trader *Trader) Run(ctx context.Context) error { router := &ExchangeOrderExecutionRouter{ // copy the parent notifiers Notifiability: trader.Notifiability, - sessions: trader.environment.sessions, + sessions: trader.environment.sessions, } for _, strategy := range trader.crossExchangeStrategies { @@ -384,7 +412,7 @@ func (trader *Trader) reportPnL() { report.Print() trader.NotifyPnL(report) } - */ +*/ /* func (trader *Trader) NotifyPnL(report *accounting.ProfitAndLossReport) { @@ -422,7 +450,6 @@ func (trader *Trader) SubmitOrder(ctx context.Context, order types.SubmitOrder) } } - type ExchangeOrderExecutionRouter struct { Notifiability @@ -442,7 +469,6 @@ func (e *ExchangeOrderExecutionRouter) SubmitOrderTo(ctx context.Context, sessio return es.Exchange.SubmitOrder(ctx, order) } - // ExchangeOrderExecutor is an order executor wrapper for single exchange instance. type ExchangeOrderExecutor struct { Notifiability diff --git a/pkg/strategy/buyandhold/cmd.go b/pkg/strategy/buyandhold/cmd.go new file mode 100644 index 000000000..d142f1e9d --- /dev/null +++ b/pkg/strategy/buyandhold/cmd.go @@ -0,0 +1,72 @@ +package buyandhold + +import ( + "context" + "syscall" + + "github.com/spf13/cobra" + + "github.com/c9s/bbgo/cmd/cmdutil" + "github.com/c9s/bbgo/pkg/bbgo" + "github.com/c9s/bbgo/pkg/types" +) + +func init() { + // flags for strategies + Cmd.Flags().String("exchange", "binance", "target exchange") + Cmd.Flags().String("symbol", "BTCUSDT", "trading symbol") + Cmd.Flags().String("interval", "1h", "kline interval for price change detection") + Cmd.Flags().Float64("base-quantity", 0.1, "base quantity of the order") +} + +var Cmd = &cobra.Command{ + Use: "buyandhold", + Short: "buy and hold", + Long: "buy and hold strategy", + + // SilenceUsage is an option to silence usage when an error occurs. + SilenceUsage: true, + + RunE: func(cmd *cobra.Command, args []string) error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + exchangeNameStr, err := cmd.Flags().GetString("exchange") + if err != nil { + return err + } + + exchangeName, err := types.ValidExchangeName(exchangeNameStr) + if err != nil { + return err + } + + symbol, err := cmd.Flags().GetString("symbol") + if err != nil { + return err + } + + interval, err := cmd.Flags().GetString("interval") + if err != nil { + return err + } + + baseQuantity, err := cmd.Flags().GetFloat64("base-quantity") + if err != nil { + return err + } + + db, err := cmdutil.ConnectMySQL() + if err != nil { + return err + } + + environ := bbgo.NewDefaultEnvironment(db) + trader := bbgo.NewTrader(environ) + trader.AttachStrategy(string(exchangeName), New(symbol, interval, baseQuantity)) + err = trader.Run(ctx) + + cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM) + return err + }, +} diff --git a/strategies/buyandhold/main.go b/strategies/buyandhold/main.go index 78a7a5e71..778839a0a 100644 --- a/strategies/buyandhold/main.go +++ b/strategies/buyandhold/main.go @@ -1,98 +1,29 @@ package main import ( - "context" "strings" - "syscall" log "github.com/sirupsen/logrus" - "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/c9s/bbgo/cmd/cmdutil" "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/strategy/buyandhold" - "github.com/c9s/bbgo/pkg/types" ) func init() { - rootCmd.PersistentFlags().String("binance-api-key", "", "binance api key") - rootCmd.PersistentFlags().String("binance-api-secret", "", "binance api secret") - rootCmd.PersistentFlags().String("max-api-key", "", "max api key") - rootCmd.PersistentFlags().String("max-api-secret", "", "max api secret") - - rootCmd.Flags().String("exchange", "binance", "target exchange") - rootCmd.Flags().String("symbol", "BTCUSDT", "trading symbol") - rootCmd.Flags().String("interval", "1h", "kline interval for price change detection") - - viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - - // Enable environment variable binding, the env vars are not overloaded yet. - viper.AutomaticEnv() - - if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil { - log.WithError(err).Errorf("failed to bind persistent flags. please check the flag settings.") - } -} - -var rootCmd = &cobra.Command{ - Use: "buyandhold", - Short: "buy and hold", - Long: "hold trader", - - // SilenceUsage is an option to silence usage when an error occurs. - SilenceUsage: true, - - RunE: func(cmd *cobra.Command, args []string) error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - exchangeNameStr, err := cmd.Flags().GetString("exchange") - if err != nil { - return err - } - - exchangeName, err := types.ValidExchangeName(exchangeNameStr) - if err != nil { - return err - } - - symbol, err := cmd.Flags().GetString("symbol") - if err != nil { - return err - } - - interval, err := cmd.Flags().GetString("interval") - if err != nil { - return err - } - - exchange, err := cmdutil.NewExchange(exchangeName) - if err != nil { - return err - } - - db, err := cmdutil.ConnectMySQL() - if err != nil { - return err - } - - sessionID := "main" - environ := bbgo.NewEnvironment(db) - environ.AddExchange(sessionID, exchange) - - trader := bbgo.NewTrader(environ) - trader.AttachStrategy(sessionID, buyandhold.New(symbol, interval, 0.1)) - // trader.AttachCrossExchangeStrategy(...) - err = trader.Run(ctx) - - cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM) - return err - }, + // persistent flags for trading environment + bbgo.PersistentFlags(buyandhold.Cmd.PersistentFlags()) } func main() { - if err := rootCmd.Execute(); err != nil { + // general viper setup for trading environment + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + viper.AutomaticEnv() + if err := viper.BindPFlags(buyandhold.Cmd.PersistentFlags()); err != nil { + log.WithError(err).Errorf("failed to bind persistent flags. please check the flag settings.") + } + + if err := buyandhold.Cmd.Execute(); err != nil { log.WithError(err).Fatalf("cannot execute command") } }