From ec23266cc20248c490d1bb62eddcfdbae6da3fb2 Mon Sep 17 00:00:00 2001 From: c9s Date: Tue, 13 Oct 2020 16:17:07 +0800 Subject: [PATCH] implement buyandhold strategy to test the api design --- pkg/bbgo/account.go | 9 ++++- pkg/strategy/buyandhold/main.go | 70 ++++++++++++++++++++++++++------- strategies/buyandhold/main.go | 7 ++-- 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/pkg/bbgo/account.go b/pkg/bbgo/account.go index 46fceb045..e6790346a 100644 --- a/pkg/bbgo/account.go +++ b/pkg/bbgo/account.go @@ -14,6 +14,13 @@ type Account struct { Balances map[string]types.Balance } +func (a *Account) Balance(currency string) (balance types.Balance, ok bool) { + a.Lock() + balance, ok = a.Balances[currency] + a.Unlock() + return balance, ok +} + func (a *Account) handleBalanceUpdates(balances map[string]types.Balance) { a.Lock() defer a.Unlock() @@ -34,7 +41,7 @@ func (a *Account) Print() { for _, balance := range a.Balances { if util.NotZero(balance.Available) { - log.Infof("[trader] balance %s %f", balance.Currency, balance.Available) + log.Infof("account balance %s %f", balance.Currency, balance.Available) } } } diff --git a/pkg/strategy/buyandhold/main.go b/pkg/strategy/buyandhold/main.go index e3b849050..5b217e057 100644 --- a/pkg/strategy/buyandhold/main.go +++ b/pkg/strategy/buyandhold/main.go @@ -2,42 +2,82 @@ package buyandhold import ( "context" + "encoding/json" + "io/ioutil" + "math" "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/types" + "github.com/c9s/bbgo/pkg/util" ) type Strategy struct { - symbol string + Symbol string `json:"symbol"` + Interval string `json:"interval"` + BaseQuantity float64 `json:"baseQuantity"` + MaxAssetQuantity float64 `json:"maxAssetQuantity"` + MinDropPercentage float64 `json:"minDropPercentage"` } -func New(symbol string) *Strategy { +func LoadFile(filepath string) (*Strategy, error) { + o, err := ioutil.ReadFile(filepath) + if err != nil { + return nil, err + } + + var strategy Strategy + err = json.Unmarshal(o, &strategy) + return &strategy, err +} + +func New(symbol string, interval string, baseQuantity float64) *Strategy { return &Strategy{ - symbol: symbol, + Symbol: symbol, + Interval: interval, + BaseQuantity: baseQuantity, + MinDropPercentage: -0.08, } } +func (s *Strategy) SetMinDropPercentage(p float64) *Strategy { + s.MinDropPercentage = p + return s +} + +func (s *Strategy) SetMaxAssetQuantity(q float64) *Strategy { + s.MaxAssetQuantity = q + return s +} + func (s *Strategy) Run(ctx context.Context, trader types.Trader, session *bbgo.ExchangeSession) error { - session.Subscribe(types.KLineChannel, s.symbol, types.SubscribeOptions{ Interval: "1h" }) + session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval}) session.Stream.OnKLineClosed(func(kline types.KLine) { changePercentage := kline.GetChange() / kline.Open - // buy when price drops -10% - if changePercentage < -0.1 { + // buy when price drops -8% + if changePercentage < s.MinDropPercentage { + market, ok := session.Markets[s.Symbol] + if !ok { + return + } + + baseBalance, ok := session.Account.Balance(market.BaseCurrency) + if ok { + // we hold too many + if util.NotZero(s.MaxAssetQuantity) && baseBalance.Available > s.MaxAssetQuantity { + return + } + } + trader.SubmitOrder(ctx, &types.SubmitOrder{ - Symbol: kline.Symbol, - Side: types.SideTypeBuy, - Type: types.OrderTypeMarket, - Quantity: 1.0, + Symbol: kline.Symbol, + Side: types.SideTypeBuy, + Type: types.OrderTypeMarket, + Quantity: s.BaseQuantity * math.Abs(changePercentage), }) } }) return nil } - - - - - diff --git a/strategies/buyandhold/main.go b/strategies/buyandhold/main.go index 07f9ace1b..860f0aea2 100644 --- a/strategies/buyandhold/main.go +++ b/strategies/buyandhold/main.go @@ -69,11 +69,12 @@ var rootCmd = &cobra.Command{ environ.AddExchange(sessionID, exchange).Subscribe(types.KLineChannel, symbol, types.SubscribeOptions{}) trader := bbgo.NewTrader(environ) - trader.AttachStrategy(sessionID, buyandhold.New(symbol)) - trader.Run(ctx) + trader.AttachStrategy(sessionID, buyandhold.New(symbol, "1h", 0.1)) + // trader.AttachCrossExchangeStrategy(...) + err = trader.Run(ctx) cmdutil.WaitForSignal(ctx, syscall.SIGINT, syscall.SIGTERM) - return nil + return err }, }