Merge pull request #848 from c9s/strategy/pivotshort

strategy/pivotshort: refactor trendEMA and add maxGradient config
This commit is contained in:
Yo-An Lin 2022-07-28 10:36:29 +08:00 committed by GitHub
commit 05f6581bcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 43 deletions

View File

@ -136,6 +136,7 @@ func (e *GeneralOrderExecutor) ClosePosition(ctx context.Context, percentage fix
return nil
}
log.Infof("closing %s position with tags: %v", e.symbol, tags)
submitOrder.Tag = strings.Join(tags, ",")
_, err := e.SubmitOrders(ctx, *submitOrder)
return err

View File

@ -15,32 +15,6 @@ type StopEMA struct {
Range fixedpoint.Value `json:"range"`
}
type TrendEMA struct {
types.IntervalWindow
trendEWMA *indicator.EWMA
trendEWMALast, trendEWMACurrent float64
}
func (s *TrendEMA) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.GeneralOrderExecutor) {
symbol := orderExecutor.Position().Symbol
s.trendEWMA = session.StandardIndicatorSet(symbol).EWMA(s.IntervalWindow)
session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) {
s.trendEWMALast = s.trendEWMACurrent
s.trendEWMACurrent = s.trendEWMA.Last()
}))
}
func (s *TrendEMA) Gradient() (float64, bool) {
if s.trendEWMALast > 0.0 && s.trendEWMACurrent > 0.0 {
gradient := s.trendEWMALast / s.trendEWMACurrent
return gradient, true
}
return 0.0, false
}
type FakeBreakStop struct {
types.IntervalWindow
}
@ -120,6 +94,9 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
}
if s.TrendEMA != nil {
if s.TrendEMA.MaxGradient == 0.0 {
s.TrendEMA.MaxGradient = 1.0
}
s.TrendEMA.Bind(session, orderExecutor)
}
@ -160,7 +137,7 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
// the kline opened below the last break low, and closed above the last break low
if k.Open.Compare(s.lastBreakLow) < 0 && k.Close.Compare(s.lastBreakLow) > 0 {
bbgo.Notify("kLine closed above the last break low, triggering stop earlier")
if err := s.orderExecutor.ClosePosition(context.Background(), one, "kLineClosedStop"); err != nil {
if err := s.orderExecutor.ClosePosition(context.Background(), one, "fakeBreakStop"); err != nil {
log.WithError(err).Error("position close error")
}
@ -213,15 +190,10 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
}
// trend EMA protection
if gradient, ok := s.TrendEMA.Gradient(); ok {
if gradient > 1.0 {
log.Debugf("trendEMA %+v current=%f last=%f slope=%f: skip short", s.TrendEMA, s.TrendEMA.trendEWMACurrent, s.TrendEMA.trendEWMALast, gradient)
if !s.TrendEMA.GradientAllowed() {
return
}
log.Debugf("trendEMA %+v current=%f last=%f slope=%f: short is enabled", s.TrendEMA, s.TrendEMA.trendEWMACurrent, s.TrendEMA.trendEWMALast, gradient)
}
// stop EMA protection
if s.stopEWMA != nil {
ema := fixedpoint.NewFromFloat(s.stopEWMA.Last())

View File

@ -58,6 +58,9 @@ func (s *ResistanceShort) Bind(session *bbgo.ExchangeSession, orderExecutor *bbg
s.activeOrders.BindStream(session.UserDataStream)
if s.TrendEMA != nil {
if s.TrendEMA.MaxGradient == 0.0 {
s.TrendEMA.MaxGradient = 1.0
}
s.TrendEMA.Bind(session, orderExecutor)
}
@ -68,15 +71,10 @@ func (s *ResistanceShort) Bind(session *bbgo.ExchangeSession, orderExecutor *bbg
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.Interval, func(kline types.KLine) {
// trend EMA protection
if gradient, ok := s.TrendEMA.Gradient(); ok {
if gradient > 1.0 {
log.Debugf("trendEMA %+v current=%f last=%f gradient=%f: skip short", s.TrendEMA, s.TrendEMA.trendEWMACurrent, s.TrendEMA.trendEWMALast, gradient)
if !s.TrendEMA.GradientAllowed() {
return
}
log.Debugf("trendEMA %+v current=%f last=%f gradient=%f: short is enabled", s.TrendEMA, s.TrendEMA.trendEWMACurrent, s.TrendEMA.trendEWMALast, gradient)
}
position := s.orderExecutor.Position()
if position.IsOpened(kline.Close) {
return

View File

@ -0,0 +1,51 @@
package pivotshort
import (
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/indicator"
"github.com/c9s/bbgo/pkg/types"
)
type TrendEMA struct {
types.IntervalWindow
// MaxGradient is the maximum gradient allowed for the entry.
MaxGradient float64 `json:"maxGradient"`
MinGradient float64 `json:"minGradient"`
trendEWMA *indicator.EWMA
trendEWMALast, trendEWMACurrent, trendGradient float64
}
func (s *TrendEMA) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.GeneralOrderExecutor) {
symbol := orderExecutor.Position().Symbol
s.trendEWMA = session.StandardIndicatorSet(symbol).EWMA(s.IntervalWindow)
session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) {
s.trendEWMALast = s.trendEWMACurrent
s.trendEWMACurrent = s.trendEWMA.Last()
}))
}
func (s *TrendEMA) GradientAllowed() bool {
if s.trendEWMALast > 0.0 && s.trendEWMACurrent > 0.0 {
s.trendGradient = s.trendEWMALast / s.trendEWMACurrent
}
if s.trendGradient == .0 {
return false
}
if s.MaxGradient > 0.0 && s.trendGradient < s.MaxGradient {
log.Debugf("trendEMA %+v current=%f last=%f gradient=%f: allowed", s, s.trendEWMACurrent, s.trendEWMALast, s.trendGradient)
return true
}
if s.MinGradient > 0.0 && s.trendGradient > s.MinGradient {
log.Debugf("trendEMA %+v current=%f last=%f gradient=%f: allowed", s, s.trendEWMACurrent, s.trendEWMALast, s.trendGradient)
return true
}
log.Debugf("trendEMA %+v current=%f last=%f gradient=%f: disallowed", s, s.trendEWMACurrent, s.trendEWMALast, s.trendGradient)
return false
}

View File

@ -207,8 +207,12 @@ func (s *TradeStats) add(pnl fixedpoint.Value) {
}
s.ProfitFactor = s.GrossProfit.Div(s.GrossLoss.Abs())
if len(s.Profits) > 0 {
s.AverageProfitTrade = fixedpoint.Avg(s.Profits)
}
if len(s.Losses) > 0 {
s.AverageLossTrade = fixedpoint.Avg(s.Losses)
}
}
// Output TradeStats without Profits and Losses