From 2c378d6047a99777c040c0ddd239f51cd3db4c0a Mon Sep 17 00:00:00 2001 From: c9s Date: Thu, 26 Aug 2021 11:31:36 +0800 Subject: [PATCH] add etf strategy --- pkg/strategy/etf/strategy.go | 106 +++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 pkg/strategy/etf/strategy.go diff --git a/pkg/strategy/etf/strategy.go b/pkg/strategy/etf/strategy.go new file mode 100644 index 000000000..bb159777e --- /dev/null +++ b/pkg/strategy/etf/strategy.go @@ -0,0 +1,106 @@ +package etf + +import ( + "context" + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "time" + + "github.com/c9s/bbgo/pkg/bbgo" + "github.com/c9s/bbgo/pkg/types" +) + +const ID = "etf" + +func init() { + bbgo.RegisterStrategy(ID, &Strategy{}) +} + +type Strategy struct { + Market types.Market + + Notifiability *bbgo.Notifiability + + + TotalAmount fixedpoint.Value `json:"totalAmount,omitempty"` + + // Interval is the period that you want to submit order + Duration types.Duration `json:"duration"` + + Index map[string]fixedpoint.Value `json:"index"` +} + +func (s *Strategy) ID() string { + return ID +} + +func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) { +} + +func (s *Strategy) Validate() error { + if s.TotalAmount == 0 { + return errors.New("amount can not be empty") + } + + return nil +} + +func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error { + go func() { + ticker := time.NewTicker(s.Duration.Duration()) + defer ticker.Stop() + + s.Notifiability.Notify("ETF orders will be executed every %s", s.Duration.Duration().String()) + + for { + select { + case <-ctx.Done(): + return + + case <-ticker.C: + totalAmount := s.TotalAmount + for symbol, ratio := range s.Index { + amount := totalAmount.Mul(ratio) + + ticker, err := session.Exchange.QueryTicker(ctx, symbol) + if err != nil { + log.WithError(err).Error("query ticker error") + } + + askPrice := fixedpoint.NewFromFloat(ticker.Sell) + quantity := askPrice.Div(amount) + + // execute orders + quoteBalance, ok := session.Account.Balance(s.Market.QuoteCurrency) + if !ok { + return + } + if quoteBalance.Available < amount { + s.Notifiability.Notify("Quote balance %s is not enough: %f < %f", s.Market.QuoteCurrency, quoteBalance.Available.Float64(), amount.Float64()) + return + } + + s.Notifiability.Notify("Submitting etf order %s quantity %f at price %f (index ratio %f %%)", + symbol, + quantity.Float64(), + askPrice.Float64(), + ratio.Float64()*100.0) + _, err = orderExecutor.SubmitOrders(ctx, types.SubmitOrder{ + Symbol: symbol, + Side: types.SideTypeBuy, + Type: types.OrderTypeMarket, + Quantity: quantity.Float64(), + }) + + if err != nil { + log.WithError(err).Error("submit order error") + } + + } + } + } + }() + + return nil +}