pivotshort: pull out stop price check to a single method

This commit is contained in:
c9s 2022-06-26 19:06:16 +08:00
parent ef31e90728
commit 3604bae933
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
4 changed files with 66 additions and 25 deletions

View File

@ -5,10 +5,8 @@ import "github.com/c9s/bbgo/pkg/bbgo"
type ExitMethod struct {
RoiStopLoss *RoiStopLoss `json:"roiStopLoss"`
ProtectionStopLoss *ProtectionStopLoss `json:"protectionStopLoss"`
RoiTakeProfit *RoiTakeProfit `json:"roiTakeProfit"`
LowerShadowTakeProfit *LowerShadowTakeProfit `json:"lowerShadowTakeProfit"`
CumulatedVolumeTakeProfit *CumulatedVolumeTakeProfit `json:"cumulatedVolumeTakeProfit"`
}

View File

@ -110,6 +110,20 @@ func (s *ProtectionStopLoss) Bind(session *bbgo.ExchangeSession, orderExecutor *
s.handleChange(context.Background(), position, kline.Close, s.orderExecutor)
}
})
if !bbgo.IsBackTesting {
session.MarketDataStream.OnMarketTrade(func(trade types.Trade) {
if trade.Symbol != position.Symbol {
return
}
if s.stopLossPrice.IsZero() || s.PlaceStopOrder {
return
}
s.checkStopPrice(trade.Price, position)
})
}
}
func (s *ProtectionStopLoss) handleChange(ctx context.Context, position *types.Position, closePrice fixedpoint.Value, orderExecutor *bbgo.GeneralOrderExecutor) {
@ -136,19 +150,27 @@ func (s *ProtectionStopLoss) handleChange(ctx context.Context, position *types.P
log.Infof("[ProtectionStopLoss] %s protection stop loss activated, current price = %f, average cost = %f, stop loss price = %f",
position.Symbol, closePrice.Float64(), position.AverageCost.Float64(), s.stopLossPrice.Float64())
if s.PlaceStopOrder {
if err := s.placeStopOrder(ctx, position, orderExecutor); err != nil {
log.WithError(err).Errorf("failed to place stop limit order")
}
return
}
} else {
// not activated, skip setup stop order
return
}
}
if s.PlaceStopOrder {
if err := s.placeStopOrder(ctx, position, orderExecutor); err != nil {
log.WithError(err).Errorf("failed to place stop limit order")
}
} else if s.shouldStop(closePrice) {
// check stop price
s.checkStopPrice(closePrice, position)
}
func (s *ProtectionStopLoss) checkStopPrice(closePrice fixedpoint.Value, position *types.Position) {
if s.shouldStop(closePrice) {
log.Infof("[ProtectionStopLoss] protection stop order is triggered at price %f, position = %+v", closePrice.Float64(), position)
if err := orderExecutor.ClosePosition(ctx, one); err != nil {
if err := s.orderExecutor.ClosePosition(context.Background(), one); err != nil {
log.WithError(err).Errorf("failed to close position")
}
}

View File

@ -25,17 +25,30 @@ func (s *RoiStopLoss) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Ge
return
}
closePrice := kline.Close
if position.IsClosed() || position.IsDust(closePrice) {
return
}
roi := position.ROI(closePrice)
if roi.Compare(s.Percentage.Neg()) < 0 {
// stop loss
bbgo.Notify("[RoiStopLoss] %s stop loss triggered by ROI %s/%s, price: %f", position.Symbol, roi.Percentage(), s.Percentage.Neg().Percentage(), kline.Close.Float64())
_ = orderExecutor.ClosePosition(context.Background(), fixedpoint.One)
return
}
s.checkStopPrice(kline.Close, position)
})
if !bbgo.IsBackTesting {
session.MarketDataStream.OnMarketTrade(func(trade types.Trade) {
if trade.Symbol != position.Symbol {
return
}
s.checkStopPrice(trade.Price, position)
})
}
}
func (s *RoiStopLoss) checkStopPrice(closePrice fixedpoint.Value, position *types.Position) {
if position.IsClosed() || position.IsDust(closePrice) {
return
}
roi := position.ROI(closePrice)
if roi.Compare(s.Percentage.Neg()) < 0 {
// stop loss
bbgo.Notify("[RoiStopLoss] %s stop loss triggered by ROI %s/%s, price: %f", position.Symbol, roi.Percentage(), s.Percentage.Neg().Percentage(), closePrice.Float64())
_ = s.orderExecutor.ClosePosition(context.Background(), fixedpoint.One)
return
}
}

View File

@ -123,6 +123,10 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
if s.BounceShort != nil && s.BounceShort.Enabled {
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.BounceShort.Interval})
}
if !bbgo.IsBackTesting {
session.Subscribe(types.MarketTradeChannel, s.Symbol, types.SubscribeOptions{})
}
}
func (s *Strategy) useQuantityOrBaseBalance(quantity fixedpoint.Value) fixedpoint.Value {
@ -189,6 +193,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
s.TradeStats = &types.TradeStats{}
}
s.lastLow = fixedpoint.Zero
// StrategyController
s.Status = types.StrategyStatusRunning
@ -230,7 +236,13 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
s.stopEWMA = standardIndicator.EWMA(*s.BreakLow.StopEMA)
}
s.lastLow = fixedpoint.Zero
for _, method := range s.ExitMethods {
method.Bind(session, s.orderExecutor)
}
session.MarketDataStream.OnMarketTrade(func(trade types.Trade) {
log.Info(trade)
})
session.UserDataStream.OnStart(func() {
lastKLine := s.preloadPivot(s.pivot, store)
@ -264,10 +276,6 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
}
})
for _, method := range s.ExitMethods {
method.Bind(session, s.orderExecutor)
}
// Always check whether you can open a short position or not
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
if s.Status != types.StrategyStatusRunning {