Merge pull request #844 from c9s/strategy/pivotshort

strategy/pivotshort: refactor and update indicator api usage
This commit is contained in:
Yo-An Lin 2022-07-26 23:43:22 +08:00 committed by GitHub
commit 605c66556e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 26 additions and 97 deletions

View File

@ -34,9 +34,3 @@ func (inc *Low) PushK(k types.KLine) {
inc.EndTime = k.EndTime.Time() inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last()) inc.EmitUpdate(inc.Last())
} }
func (inc *Low) LoadK(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
}
}

View File

@ -60,42 +60,22 @@ func (inc *MACD) Last() float64 {
return inc.Values[len(inc.Values)-1] return inc.Values[len(inc.Values)-1]
} }
func (inc *MACD) Length() int {
return len(inc.Values)
}
func (inc *MACD) PushK(k types.KLine) { func (inc *MACD) PushK(k types.KLine) {
inc.Update(k.Close.Float64()) inc.Update(k.Close.Float64())
} }
func (inc *MACD) CalculateAndUpdate(allKLines []types.KLine) { func (inc *MACD) MACD() types.SeriesExtend {
if len(allKLines) == 0 { out := &MACDValues{MACD: inc}
return out.SeriesBase.Series = out
} return out
last := allKLines[len(allKLines)-1]
if len(inc.Values) == 0 {
for _, k := range allKLines {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
continue
}
inc.PushK(k)
}
} else {
inc.PushK(last)
}
inc.EmitUpdate(inc.Last())
inc.EndTime = last.EndTime.Time()
} }
func (inc *MACD) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) { func (inc *MACD) Singals() types.SeriesExtend {
if inc.Interval != interval { return inc.SignalLine
return
}
inc.CalculateAndUpdate(window)
}
func (inc *MACD) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
} }
type MACDValues struct { type MACDValues struct {
@ -123,13 +103,3 @@ func (inc *MACDValues) Index(i int) float64 {
func (inc *MACDValues) Length() int { func (inc *MACDValues) Length() int {
return len(inc.Values) return len(inc.Values)
} }
func (inc *MACD) MACD() types.SeriesExtend {
out := &MACDValues{MACD: inc}
out.SeriesBase.Series = out
return out
}
func (inc *MACD) Singals() types.SeriesExtend {
return inc.SignalLine
}

View File

@ -41,7 +41,9 @@ func Test_calculateMACD(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
iw := types.IntervalWindow{Window: 9} iw := types.IntervalWindow{Window: 9}
macd := MACD{IntervalWindow: iw, ShortPeriod: 12, LongPeriod: 26} macd := MACD{IntervalWindow: iw, ShortPeriod: 12, LongPeriod: 26}
macd.CalculateAndUpdate(tt.kLines) for _, k := range tt.kLines {
macd.PushK(k)
}
got := macd.Last() got := macd.Last()
diff := math.Trunc((got-tt.want)*100) / 100 diff := math.Trunc((got-tt.want)*100) / 100

View File

@ -34,7 +34,7 @@ type BreakLow struct {
TrendEMA *types.IntervalWindow `json:"trendEMA"` TrendEMA *types.IntervalWindow `json:"trendEMA"`
lastLow fixedpoint.Value lastLow fixedpoint.Value
pivot *indicator.Pivot pivot *indicator.PivotLow
stopEWMA *indicator.EWMA stopEWMA *indicator.EWMA
trendEWMA *indicator.EWMA trendEWMA *indicator.EWMA
@ -65,14 +65,11 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
position := orderExecutor.Position() position := orderExecutor.Position()
symbol := position.Symbol symbol := position.Symbol
store, _ := session.MarketDataStore(s.Symbol)
standardIndicator := session.StandardIndicatorSet(s.Symbol) standardIndicator := session.StandardIndicatorSet(s.Symbol)
s.lastLow = fixedpoint.Zero s.lastLow = fixedpoint.Zero
s.pivot = &indicator.Pivot{IntervalWindow: s.IntervalWindow} s.pivot = standardIndicator.PivotLow(s.IntervalWindow)
s.pivot.Bind(store)
preloadPivot(s.pivot, store)
if s.StopEMA != nil { if s.StopEMA != nil {
s.stopEWMA = standardIndicator.EWMA(*s.StopEMA) s.stopEWMA = standardIndicator.EWMA(*s.StopEMA)
@ -89,13 +86,13 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
// update pivot low data // update pivot low data
session.MarketDataStream.OnStart(func() { session.MarketDataStream.OnStart(func() {
lastLow := fixedpoint.NewFromFloat(s.pivot.LastLow()) lastLow := fixedpoint.NewFromFloat(s.pivot.Lows.Last())
if lastLow.IsZero() { if lastLow.IsZero() {
return return
} }
if lastLow.Compare(s.lastLow) != 0 { if lastLow.Compare(s.lastLow) != 0 {
bbgo.Notify("%s found new pivot low: %f", s.Symbol, s.pivot.LastLow()) bbgo.Notify("%s found new pivot low: %f", s.Symbol, s.pivot.Lows.Last())
} }
s.lastLow = lastLow s.lastLow = lastLow
@ -120,8 +117,7 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
}) })
session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) { session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) {
lastLow := fixedpoint.NewFromFloat(s.pivot.Lows.Last())
lastLow := fixedpoint.NewFromFloat(s.pivot.LastLow())
if lastLow.IsZero() { if lastLow.IsZero() {
return return
} }
@ -133,13 +129,12 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
s.lastLow = lastLow s.lastLow = lastLow
s.pivotLowPrices = append(s.pivotLowPrices, s.lastLow) s.pivotLowPrices = append(s.pivotLowPrices, s.lastLow)
// when position is opened, do not send pivot low notify // when position is opened, do not send pivot low notify
if position.IsOpened(kline.Close) { if position.IsOpened(kline.Close) {
return return
} }
bbgo.Notify("%s new pivot low: %f", s.Symbol, s.pivot.LastLow()) bbgo.Notify("%s new pivot low: %f", s.Symbol, s.pivot.Lows.Last())
})) }))
session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, types.Interval1m, func(kline types.KLine) { session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, types.Interval1m, func(kline types.KLine) {
@ -246,4 +241,3 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
} }
})) }))
} }

View File

@ -26,7 +26,7 @@ type ResistanceShort struct {
session *bbgo.ExchangeSession session *bbgo.ExchangeSession
orderExecutor *bbgo.GeneralOrderExecutor orderExecutor *bbgo.GeneralOrderExecutor
resistancePivot *indicator.Pivot resistancePivot *indicator.PivotLow
resistancePrices []float64 resistancePrices []float64
currentResistancePrice fixedpoint.Value currentResistancePrice fixedpoint.Value
@ -47,19 +47,10 @@ func (s *ResistanceShort) Bind(session *bbgo.ExchangeSession, orderExecutor *bbg
s.GroupDistance = fixedpoint.NewFromFloat(0.01) s.GroupDistance = fixedpoint.NewFromFloat(0.01)
} }
store, _ := session.MarketDataStore(s.Symbol) s.resistancePivot = session.StandardIndicatorSet(s.Symbol).PivotLow(s.IntervalWindow)
s.resistancePivot = &indicator.Pivot{IntervalWindow: s.IntervalWindow}
s.resistancePivot.Bind(store)
// preload history kline data to the resistance pivot indicator
// we use the last kline to find the higher lows
lastKLine := preloadPivot(s.resistancePivot, store)
// use the last kline from the history before we get the next closed kline // use the last kline from the history before we get the next closed kline
if lastKLine != nil { s.updateResistanceOrders(fixedpoint.NewFromFloat(s.resistancePivot.Lows.Last()))
s.updateResistanceOrders(lastKLine.Close)
}
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.Interval, func(kline types.KLine) { session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.Interval, func(kline types.KLine) {
position := s.orderExecutor.Position() position := s.orderExecutor.Position()

View File

@ -35,7 +35,7 @@ type SupportTakeProfit struct {
Ratio fixedpoint.Value `json:"ratio"` Ratio fixedpoint.Value `json:"ratio"`
pivot *indicator.Pivot pivot *indicator.PivotLow
orderExecutor *bbgo.GeneralOrderExecutor orderExecutor *bbgo.GeneralOrderExecutor
session *bbgo.ExchangeSession session *bbgo.ExchangeSession
activeOrders *bbgo.ActiveOrderBook activeOrders *bbgo.ActiveOrderBook
@ -62,11 +62,8 @@ func (s *SupportTakeProfit) Bind(session *bbgo.ExchangeSession, orderExecutor *b
s.activeOrders.BindStream(session.UserDataStream) s.activeOrders.BindStream(session.UserDataStream)
position := orderExecutor.Position() position := orderExecutor.Position()
symbol := position.Symbol
store, _ := session.MarketDataStore(symbol) s.pivot = session.StandardIndicatorSet(s.Symbol).PivotLow(s.IntervalWindow)
s.pivot = &indicator.Pivot{IntervalWindow: s.IntervalWindow}
s.pivot.Bind(store)
preloadPivot(s.pivot, store)
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.Interval, func(kline types.KLine) { session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.Interval, func(kline types.KLine) {
if !s.updateSupportPrice(kline.Close) { if !s.updateSupportPrice(kline.Close) {
@ -88,7 +85,7 @@ func (s *SupportTakeProfit) Bind(session *bbgo.ExchangeSession, orderExecutor *b
bbgo.Notify("placing %s take profit order at price %f", s.Symbol, buyPrice.Float64()) bbgo.Notify("placing %s take profit order at price %f", s.Symbol, buyPrice.Float64())
createdOrders, err := orderExecutor.SubmitOrders(ctx, types.SubmitOrder{ createdOrders, err := orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
Symbol: symbol, Symbol: s.Symbol,
Type: types.OrderTypeLimitMaker, Type: types.OrderTypeLimitMaker,
Side: types.SideTypeBuy, Side: types.SideTypeBuy,
Price: buyPrice, Price: buyPrice,
@ -215,7 +212,6 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
s.ExitMethods.SetAndSubscribe(session, s) s.ExitMethods.SetAndSubscribe(session, s)
} }
func (s *Strategy) CurrentPosition() *types.Position { func (s *Strategy) CurrentPosition() *types.Position {
return s.Position return s.Position
} }
@ -293,21 +289,3 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
return nil return nil
} }
func preloadPivot(pivot *indicator.Pivot, store *bbgo.MarketDataStore) *types.KLine {
klines, ok := store.KLinesOfInterval(pivot.Interval)
if !ok {
return nil
}
last := (*klines)[len(*klines)-1]
log.Debugf("updating pivot indicator: %d klines", len(*klines))
for i := pivot.Window; i < len(*klines); i++ {
pivot.CalculateAndUpdate((*klines)[0 : i+1])
}
log.Debugf("found %v previous lows: %v", pivot.IntervalWindow, pivot.Lows)
log.Debugf("found %v previous highs: %v", pivot.IntervalWindow, pivot.Highs)
return &last
}