From 54d60b98907c7d08ce15146df8f36e789b1aaba5 Mon Sep 17 00:00:00 2001 From: austin362667 Date: Mon, 30 May 2022 15:20:07 +0800 Subject: [PATCH] bollmaker: add position stack --- config/bollmaker.yaml | 57 +++++++++++++++++------------- pkg/strategy/bollmaker/strategy.go | 37 ++++++++++++++----- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/config/bollmaker.yaml b/config/bollmaker.yaml index f4b253d3f..437c3f80f 100644 --- a/config/bollmaker.yaml +++ b/config/bollmaker.yaml @@ -16,8 +16,8 @@ backtest: # for testing max draw down (MDD) at 03-12 # see here for more details # https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp - startTime: "2022-01-01" - endTime: "2022-05-12" + startTime: "2022-05-01" + endTime: "2022-05-31" sessions: - binance symbols: @@ -26,7 +26,7 @@ backtest: binance: balances: ETH: 0.0 - USDT: 10_000.0 + USDT: 100_000.0 exchangeStrategies: @@ -38,7 +38,14 @@ exchangeStrategies: interval: 1m # quantity is the base order quantity for your buy/sell order. - quantity: 0.05 + # quantity: 0.05 + amount: 20 + + # Position Stack, with longer stack length, may need more capital. + # Push position in stack is initiating a position to calculate base, average cost, etc. + # Pop position in stack is loading a previous position back. + pushThreshold: 10% + # popThreshold : 1% # useTickerPrice use the ticker api to get the mid price instead of the closed kline price. # The back-test engine is kline-based, so the ticker price api is not supported. @@ -103,7 +110,7 @@ exchangeStrategies: domain: [ -1, 1 ] # when in down band, holds 1.0 by maximum # when in up band, holds 0.05 by maximum - range: [ 10.0, 1.0 ] + range: [10.0, 1.0 ] # DisableShort means you can don't want short position during the market making # THe short here means you might sell some of your existing inventory. @@ -136,25 +143,25 @@ exchangeStrategies: # Set up your stop order, this is optional # sometimes the stop order might decrease your total profit. # you can setup multiple stop, - stops: +# stops: # use trailing stop order - - trailingStop: - # callbackRate: when the price reaches -1% from the previous highest, we trigger the stop - callbackRate: 5.1% - - # closePosition is how much position do you want to close - closePosition: 20% - - # minProfit is how much profit you want to take. - # if you set this option, your stop will only be triggered above the average cost. - minProfit: 5% - - # interval is the time interval for checking your stop - interval: 1m - - # virtual means we don't place a a REAL stop order - # when virtual is on - # the strategy won't place a REAL stop order, instead if watches the close price, - # and if the condition matches, it submits a market order to close your position. - virtual: true +# - trailingStop: +# # callbackRate: when the price reaches -1% from the previous highest, we trigger the stop +# callbackRate: 5.1% +# +# # closePosition is how much position do you want to close +# closePosition: 20% +# +# # minProfit is how much profit you want to take. +# # if you set this option, your stop will only be triggered above the average cost. +# minProfit: 5% +# +# # interval is the time interval for checking your stop +# interval: 1m +# +# # virtual means we don't place a a REAL stop order +# # when virtual is on +# # the strategy won't place a REAL stop order, instead if watches the close price, +# # and if the condition matches, it submits a market order to close your position. +# virtual: true diff --git a/pkg/strategy/bollmaker/strategy.go b/pkg/strategy/bollmaker/strategy.go index 038f2c901..6db8e1119 100644 --- a/pkg/strategy/bollmaker/strategy.go +++ b/pkg/strategy/bollmaker/strategy.go @@ -228,8 +228,11 @@ type Strategy struct { state *State // persistence fields - Position *types.Position `json:"position,omitempty" persistence:"position"` - ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"` + Position *types.PositionStack `json:"position,omitempty" persistence:"position"` + ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"` + + PushThreshold fixedpoint.Value `json:"pushThreshold,omitempty"` + PopThreshold fixedpoint.Value `json:"popThreshold,omitempty"` activeMakerOrders *bbgo.LocalActiveOrderBook orderStore *bbgo.OrderStore @@ -289,7 +292,7 @@ func (s *Strategy) Validate() error { return nil } -func (s *Strategy) CurrentPosition() *types.Position { +func (s *Strategy) CurrentPosition() *types.PositionStack { return s.Position } @@ -652,9 +655,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se if s.Position == nil { // fallback to the legacy position struct in the state if s.state != nil && s.state.Position != nil { - s.Position = s.state.Position + s.Position.Position = s.state.Position } else { - s.Position = types.NewPositionFromMarket(s.Market) + s.Position = types.NewPositionStackFromMarket(s.Market) } } @@ -699,7 +702,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se s.ProfitStats.AddTrade(trade) if profit.Compare(fixedpoint.Zero) == 0 { - s.Environment.RecordPosition(s.Position, trade, nil) + s.Environment.RecordPosition(s.Position.Position, trade, nil) } else { log.Infof("%s generated profit: %v", s.Symbol, profit) p := s.Position.NewProfit(trade, profit, netProfit) @@ -710,11 +713,11 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se s.ProfitStats.AddProfit(p) s.Notify(&s.ProfitStats) - s.Environment.RecordPosition(s.Position, trade, &p) + s.Environment.RecordPosition(s.Position.Position, trade, &p) } }) - s.tradeCollector.OnPositionUpdate(func(position *types.Position) { + s.tradeCollector.OnPositionUpdate(func(position types.PositionInterface) { log.Infof("position changed: %s", s.Position) s.Notify(s.Position) }) @@ -772,7 +775,25 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil { log.WithError(err).Errorf("graceful cancel order error") } + //log.Error(len(s.Position.Stack), s.Position.AverageCost, kline.Close) + if s.Position.Position.AverageCost.Div(kline.Close).Compare(fixedpoint.One.Add(s.PushThreshold)) > 0 { + log.Errorf("push") + log.Error(s.Position) + s.Position = s.Position.Push(types.NewPositionFromMarket(s.Market)) + } + // && + if len(s.Position.Stack) > 1 && s.Position.Stack[len(s.Position.Stack)-2].AverageCost.Compare(kline.Close) < 0 && s.Market.IsDustQuantity(s.Position.Position.GetBase(), kline.Close) { + log.Errorf("pop") + log.Error(s.Position) + s.Position = s.Position.Pop() + } + //if s.Position.AverageCost.Div(kline.Close).Compare(fixedpoint.One.Sub(s.PopThreshold)) < 0 && && !s.Position.AverageCost.IsZero() { + // //log.Error(len(s.Position.Stack), s.Position.AverageCost, kline.Close) + // log.Errorf("pop") + // s.ClosePosition(ctx, fixedpoint.One) + // s.Position = s.Position.Pop() + //} // check if there is a canceled order had partially filled. s.tradeCollector.Process()