From 6a07af80d811204c483b1dd21e7604cfa502dc77 Mon Sep 17 00:00:00 2001 From: c9s Date: Tue, 19 Dec 2023 22:04:24 +0800 Subject: [PATCH 1/5] bollmaker: define EMACrossSetting --- pkg/strategy/bollmaker/strategy.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/pkg/strategy/bollmaker/strategy.go b/pkg/strategy/bollmaker/strategy.go index 05e7d3c2e..a6e3dd8ae 100644 --- a/pkg/strategy/bollmaker/strategy.go +++ b/pkg/strategy/bollmaker/strategy.go @@ -6,12 +6,11 @@ import ( "math" "sync" - indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" - "github.com/c9s/bbgo/pkg/util" - "github.com/pkg/errors" "github.com/sirupsen/logrus" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" + "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/types" @@ -46,6 +45,12 @@ type BollingerSetting struct { BandWidth float64 `json:"bandWidth"` } +type EMACrossSetting struct { + Interval types.Interval `json:"interval"` + FastWindow int `json:"fastWindow"` + SlowWindow int `json:"slowWindow"` +} + type Strategy struct { Environment *bbgo.Environment StandardIndicatorSet *bbgo.StandardIndicatorSet @@ -121,6 +126,9 @@ type Strategy struct { // BuyBelowNeutralSMA if true, the market maker will only place buy order when the current price is below the neutral band SMA. BuyBelowNeutralSMA bool `json:"buyBelowNeutralSMA"` + // EMACrossSetting is used for defining ema cross signal to turn on/off buy + EMACrossSetting *EMACrossSetting `json:"emaCross"` + // NeutralBollinger is the smaller range of the bollinger band // If price is in this band, it usually means the price is oscillating. // If price goes out of this band, we tend to not place sell orders or buy orders @@ -150,19 +158,16 @@ type Strategy struct { ShadowProtection bool `json:"shadowProtection"` ShadowProtectionRatio fixedpoint.Value `json:"shadowProtectionRatio"` - session *bbgo.ExchangeSession - book *types.StreamOrderBook - ExitMethods bbgo.ExitMethodSet `json:"exits"` // persistence fields Position *types.Position `json:"position,omitempty" persistence:"position"` ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"` + session *bbgo.ExchangeSession + book *types.StreamOrderBook orderExecutor *bbgo.GeneralOrderExecutor - groupID uint32 - // defaultBoll is the BOLLINGER indicator we used for predicting the price. defaultBoll *indicatorv2.BOLLStream @@ -274,7 +279,6 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k Quantity: sellQuantity, Price: askPrice, Market: s.Market, - GroupID: s.groupID, } buyOrder := types.SubmitOrder{ Symbol: s.Symbol, @@ -283,7 +287,6 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k Quantity: buyQuantity, Price: bidPrice, Market: s.Market, - GroupID: s.groupID, } var submitOrders []types.SubmitOrder @@ -511,7 +514,6 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se // calculate group id for orders instanceID := s.InstanceID() - s.groupID = util.FNV32(instanceID) // If position is nil, we need to allocate a new position for calculation if s.Position == nil { From 46329c3a243e845cfee2fff30a233d63b2cd5181 Mon Sep 17 00:00:00 2001 From: c9s Date: Tue, 19 Dec 2023 22:17:33 +0800 Subject: [PATCH 2/5] bollmaker: add ema cross signal to bollmaker strategy --- config/bollmaker.yaml | 8 ++++++- pkg/strategy/bollmaker/strategy.go | 34 ++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/config/bollmaker.yaml b/config/bollmaker.yaml index 0cc53894c..d2794d94f 100644 --- a/config/bollmaker.yaml +++ b/config/bollmaker.yaml @@ -17,7 +17,7 @@ backtest: # see here for more details # https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp startTime: "2022-05-01" - endTime: "2022-08-14" + endTime: "2023-11-01" sessions: - binance symbols: @@ -200,6 +200,12 @@ exchangeStrategies: # buyBelowNeutralSMA: when this set, it will only place buy order when the current price is below the SMA line. buyBelowNeutralSMA: true + emaCross: + enabled: true + interval: 1h + fastWindow: 3 + slowWindow: 12 + exits: # roiTakeProfit is used to force taking profit by percentage of the position ROI (currently the price change) diff --git a/pkg/strategy/bollmaker/strategy.go b/pkg/strategy/bollmaker/strategy.go index a6e3dd8ae..de0b92c13 100644 --- a/pkg/strategy/bollmaker/strategy.go +++ b/pkg/strategy/bollmaker/strategy.go @@ -46,9 +46,13 @@ type BollingerSetting struct { } type EMACrossSetting struct { + Enabled bool `json:"enabled"` Interval types.Interval `json:"interval"` FastWindow int `json:"fastWindow"` SlowWindow int `json:"slowWindow"` + + fastEMA, slowEMA *indicatorv2.EWMAStream + cross *indicatorv2.CrossStream } type Strategy struct { @@ -174,6 +178,8 @@ type Strategy struct { // neutralBoll is the neutral price section neutralBoll *indicatorv2.BOLLStream + shouldBuy bool + // StrategyController bbgo.StrategyController } @@ -280,6 +286,7 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k Price: askPrice, Market: s.Market, } + buyOrder := types.SubmitOrder{ Symbol: s.Symbol, Side: types.SideTypeBuy, @@ -438,14 +445,20 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k } } + if !s.shouldBuy { + log.Infof("shouldBuy is turned off, skip placing buy order") + canBuy = false + } + if canSell { submitOrders = append(submitOrders, sellOrder) } + if canBuy { submitOrders = append(submitOrders, buyOrder) } - // condition for lower the average cost + // condition for lowering the average cost /* if midPrice < s.Position.AverageCost.MulFloat64(1.0-s.MinProfitSpread.Float64()) && canBuy { submitOrders = append(submitOrders, buyOrder) @@ -481,9 +494,26 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se // StrategyController s.Status = types.StrategyStatusRunning + s.shouldBuy = true s.neutralBoll = session.Indicators(s.Symbol).BOLL(s.NeutralBollinger.IntervalWindow, s.NeutralBollinger.BandWidth) s.defaultBoll = session.Indicators(s.Symbol).BOLL(s.DefaultBollinger.IntervalWindow, s.DefaultBollinger.BandWidth) + if s.EMACrossSetting != nil && s.EMACrossSetting.Enabled { + s.EMACrossSetting.fastEMA = session.Indicators(s.Symbol).EWMA(types.IntervalWindow{Interval: s.Interval, Window: s.EMACrossSetting.FastWindow}) + s.EMACrossSetting.slowEMA = session.Indicators(s.Symbol).EWMA(types.IntervalWindow{Interval: s.Interval, Window: s.EMACrossSetting.SlowWindow}) + s.EMACrossSetting.cross = indicatorv2.Cross(s.EMACrossSetting.fastEMA, s.EMACrossSetting.slowEMA) + s.EMACrossSetting.cross.OnUpdate(func(v float64) { + switch indicatorv2.CrossType(v) { + case indicatorv2.CrossOver: + s.shouldBuy = true + case indicatorv2.CrossUnder: + s.shouldBuy = false + // TODO: can partially close position when necessary + // s.orderExecutor.ClosePosition(ctx) + } + }) + } + // Setup dynamic spread if s.DynamicSpread.IsEnabled() { if s.DynamicSpread.Interval == "" { @@ -565,7 +595,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se }) session.UserDataStream.OnStart(func() { - if s.UseTickerPrice { + if !bbgo.IsBackTesting && s.UseTickerPrice { ticker, err := s.session.Exchange.QueryTicker(ctx, s.Symbol) if err != nil { return From 127d4484fbef7b1446d5455a530030d2d0b672aa Mon Sep 17 00:00:00 2001 From: c9s Date: Tue, 19 Dec 2023 22:17:53 +0800 Subject: [PATCH 3/5] config: disable emaCross by default --- config/bollmaker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/bollmaker.yaml b/config/bollmaker.yaml index d2794d94f..ac42aeed7 100644 --- a/config/bollmaker.yaml +++ b/config/bollmaker.yaml @@ -201,7 +201,7 @@ exchangeStrategies: buyBelowNeutralSMA: true emaCross: - enabled: true + enabled: false interval: 1h fastWindow: 3 slowWindow: 12 From 311ba3b2ac1d0389a059da9445ffae810e5a7154 Mon Sep 17 00:00:00 2001 From: c9s Date: Wed, 20 Dec 2023 12:09:19 +0800 Subject: [PATCH 4/5] bollmaker: fix ema cross subscription --- pkg/strategy/bollmaker/strategy.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/strategy/bollmaker/strategy.go b/pkg/strategy/bollmaker/strategy.go index de0b92c13..e372eea1f 100644 --- a/pkg/strategy/bollmaker/strategy.go +++ b/pkg/strategy/bollmaker/strategy.go @@ -213,6 +213,10 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) { session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.TrendEMA.Interval}) } + if s.EMACrossSetting != nil && s.EMACrossSetting.Enabled { + session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.EMACrossSetting.Interval}) + } + s.ExitMethods.SetAndSubscribe(session, s) } From 382a78949c54525e3ef7cf18c1f83700bbb3b765 Mon Sep 17 00:00:00 2001 From: c9s Date: Wed, 20 Dec 2023 12:13:09 +0800 Subject: [PATCH 5/5] config: document emaCross in config --- config/bollmaker.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/bollmaker.yaml b/config/bollmaker.yaml index ac42aeed7..5ecc67315 100644 --- a/config/bollmaker.yaml +++ b/config/bollmaker.yaml @@ -200,6 +200,9 @@ exchangeStrategies: # buyBelowNeutralSMA: when this set, it will only place buy order when the current price is below the SMA line. buyBelowNeutralSMA: true + # emaCross is used for turning buy on/off + # when short term EMA cross fast term EMA, turn on buy, + # otherwise, turn off buy emaCross: enabled: false interval: 1h