Merge pull request #720 from andycheng123/fix/supertrend

fix: fix strategy supertrend
This commit is contained in:
Andy Cheng 2022-06-17 10:26:09 +08:00 committed by GitHub
commit 5c8cc397f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 20 deletions

View File

@ -1,12 +1,15 @@
package indicator package indicator
import ( import (
"github.com/sirupsen/logrus"
"math" "math"
"time" "time"
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
var logst = logrus.WithField("indicator", "supertrend")
//go:generate callbackgen -type Supertrend //go:generate callbackgen -type Supertrend
type Supertrend struct { type Supertrend struct {
types.IntervalWindow types.IntervalWindow
@ -91,7 +94,9 @@ func (inc *Supertrend) Update(highPrice, lowPrice, closePrice float64) {
} }
// Update signal // Update signal
if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown { if inc.AverageTrueRange.Last() <= 0 {
inc.tradeSignal = types.DirectionNone
} else if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown {
inc.tradeSignal = types.DirectionUp inc.tradeSignal = types.DirectionUp
} else if inc.trend == types.DirectionDown && inc.previousTrend == types.DirectionUp { } else if inc.trend == types.DirectionDown && inc.previousTrend == types.DirectionUp {
inc.tradeSignal = types.DirectionDown inc.tradeSignal = types.DirectionDown
@ -105,6 +110,10 @@ func (inc *Supertrend) Update(highPrice, lowPrice, closePrice float64) {
} else { } else {
inc.trendPrices.Push(inc.uptrendPrice) inc.trendPrices.Push(inc.uptrendPrice)
} }
logst.Debugf("Update supertrend result: closePrice: %v, uptrendPrice: %v, downtrendPrice: %v, trend: %v,"+
" tradeSignal: %v, AverageTrueRange.Last(): %v", inc.closePrice, inc.uptrendPrice, inc.downtrendPrice,
inc.trend, inc.tradeSignal, inc.AverageTrueRange.Last())
} }
func (inc *Supertrend) GetSignal() types.Direction { func (inc *Supertrend) GetSignal() types.Direction {

View File

@ -134,16 +134,18 @@ func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Valu
} }
if quantity.Compare(s.Market.MinQuantity) < 0 { if quantity.Compare(s.Market.MinQuantity) < 0 {
return fmt.Errorf("order quantity %v is too small, less than %v", quantity, s.Market.MinQuantity) return fmt.Errorf("%s order quantity %v is too small, less than %v", s.Symbol, quantity, s.Market.MinQuantity)
} }
orderForm := s.generateOrderForm(side, quantity, types.SideEffectTypeAutoRepay) orderForm := s.generateOrderForm(side, quantity, types.SideEffectTypeAutoRepay)
s.Notify("Submitting %s %s order to close position by %v", s.Symbol, side.String(), percentage, orderForm) log.Infof("submit close position order %v", orderForm)
s.Notify("Submitting %s %s order to close position by %v", s.Symbol, side.String(), percentage)
createdOrders, err := s.session.Exchange.SubmitOrders(ctx, orderForm) createdOrders, err := s.session.Exchange.SubmitOrders(ctx, orderForm)
if err != nil { if err != nil {
log.WithError(err).Errorf("can not place position close order") log.WithError(err).Errorf("can not place %s position close order", s.Symbol)
s.Notify("can not place %s position close order", s.Symbol)
} }
s.orderStore.Add(createdOrders...) s.orderStore.Add(createdOrders...)
@ -211,7 +213,7 @@ func (s *Strategy) generateOrderForm(side types.SideType, quantity fixedpoint.Va
func (s *Strategy) calculateQuantity(currentPrice fixedpoint.Value) fixedpoint.Value { func (s *Strategy) calculateQuantity(currentPrice fixedpoint.Value) fixedpoint.Value {
balance, ok := s.session.GetAccount().Balance(s.Market.QuoteCurrency) balance, ok := s.session.GetAccount().Balance(s.Market.QuoteCurrency)
if !ok { if !ok {
log.Error("can not update balance from exchange") log.Errorf("can not update %s balance from exchange", s.Symbol)
return fixedpoint.Zero return fixedpoint.Zero
} }
@ -295,27 +297,27 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
// TP/SL if there's non-dust position // TP/SL if there's non-dust position
if !s.Market.IsDustQuantity(base.Abs(), kline.GetClose()) { if !s.Market.IsDustQuantity(base.Abs(), kline.GetClose()) {
if s.StopLossByTriggeringK && !s.currentStopLossPrice.IsZero() && ((baseSign < 0 && kline.GetClose().Compare(s.currentStopLossPrice) > 0) || (baseSign > 0 && kline.GetClose().Compare(s.currentStopLossPrice) < 0)) { if s.StopLossByTriggeringK && !s.currentStopLossPrice.IsZero() && ((baseSign < 0 && kline.GetClose().Compare(s.currentStopLossPrice) > 0) || (baseSign > 0 && kline.GetClose().Compare(s.currentStopLossPrice) < 0)) {
// SL by triggered Kline low // SL by triggering Kline low
if err := s.ClosePosition(ctx, fixedpoint.One); err != nil { log.Infof("%s SL by triggering Kline low", s.Symbol)
s.Notify("can not place SL order") s.Notify("%s SL by triggering Kline low", s.Symbol)
} else { if err := s.ClosePosition(ctx, fixedpoint.One); err == nil {
s.currentStopLossPrice = fixedpoint.Zero s.currentStopLossPrice = fixedpoint.Zero
s.currentTakeProfitPrice = fixedpoint.Zero s.currentTakeProfitPrice = fixedpoint.Zero
} }
} else if s.TakeProfitMultiplier > 0 && !s.currentTakeProfitPrice.IsZero() && ((baseSign < 0 && kline.GetClose().Compare(s.currentTakeProfitPrice) < 0) || (baseSign > 0 && kline.GetClose().Compare(s.currentTakeProfitPrice) > 0)) { } else if s.TakeProfitMultiplier > 0 && !s.currentTakeProfitPrice.IsZero() && ((baseSign < 0 && kline.GetClose().Compare(s.currentTakeProfitPrice) < 0) || (baseSign > 0 && kline.GetClose().Compare(s.currentTakeProfitPrice) > 0)) {
// TP by multiple of ATR // TP by multiple of ATR
if err := s.ClosePosition(ctx, fixedpoint.One); err != nil { log.Infof("%s TP by multiple of ATR", s.Symbol)
s.Notify("can not place TP order") s.Notify("%s TP by multiple of ATR", s.Symbol)
} else { if err := s.ClosePosition(ctx, fixedpoint.One); err == nil {
s.currentStopLossPrice = fixedpoint.Zero s.currentStopLossPrice = fixedpoint.Zero
s.currentTakeProfitPrice = fixedpoint.Zero s.currentTakeProfitPrice = fixedpoint.Zero
} }
} else if s.TPSLBySignal { } else if s.TPSLBySignal {
// Use signals to TP/SL // Use signals to TP/SL
log.Infof("%s TP/SL by reverse of DEMA or Supertrend", s.Symbol)
s.Notify("%s TP/SL by reverse of DEMA or Supertrend", s.Symbol)
if (baseSign < 0 && (stSignal == types.DirectionUp || demaSignal == types.DirectionUp)) || (baseSign > 0 && (stSignal == types.DirectionDown || demaSignal == types.DirectionDown)) { if (baseSign < 0 && (stSignal == types.DirectionUp || demaSignal == types.DirectionUp)) || (baseSign > 0 && (stSignal == types.DirectionDown || demaSignal == types.DirectionDown)) {
if err := s.ClosePosition(ctx, fixedpoint.One); err != nil { if err := s.ClosePosition(ctx, fixedpoint.One); err == nil {
s.Notify("can not place TP/SL order")
} else {
s.currentStopLossPrice = fixedpoint.Zero s.currentStopLossPrice = fixedpoint.Zero
s.currentTakeProfitPrice = fixedpoint.Zero s.currentTakeProfitPrice = fixedpoint.Zero
} }
@ -343,11 +345,20 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
} }
} }
// The default value of side is an empty string. Unless side is set by the checks above, the result of the following condition is false
if side == types.SideTypeSell || side == types.SideTypeBuy { if side == types.SideTypeSell || side == types.SideTypeBuy {
log.Infof("open %s position for signal %v", s.Symbol, side)
s.Notify("open %s position for signal %v", s.Symbol, side)
// Close opposite position if any // Close opposite position if any
if !s.Market.IsDustQuantity(base.Abs(), kline.GetClose()) && ((side == types.SideTypeSell && baseSign > 0) || (side == types.SideTypeBuy && baseSign < 0)) { if !s.Position.IsDust(kline.GetClose()) {
if err := s.ClosePosition(ctx, fixedpoint.One); err != nil { if (side == types.SideTypeSell && s.Position.IsLong()) || (side == types.SideTypeBuy && s.Position.IsShort()) {
s.Notify("can not place close position order") log.Infof("close existing %s position before open a new position", s.Symbol)
s.Notify("close existing %s position before open a new position", s.Symbol)
_ = s.ClosePosition(ctx, fixedpoint.One)
} else {
log.Infof("existing %s position has the same direction with the signal", s.Symbol)
s.Notify("existing %s position has the same direction with the signal", s.Symbol)
return
} }
} }
@ -355,8 +366,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
log.Infof("submit open position order %v", orderForm) log.Infof("submit open position order %v", orderForm)
order, err := orderExecutor.SubmitOrders(ctx, orderForm) order, err := orderExecutor.SubmitOrders(ctx, orderForm)
if err != nil { if err != nil {
log.WithError(err).Errorf("can not place open position order") log.WithError(err).Errorf("can not place %s open position order", s.Symbol)
s.Notify("can not place open position order") s.Notify("can not place %s open position order", s.Symbol)
} else { } else {
s.orderStore.Add(order...) s.orderStore.Add(order...)
} }