From 017dd4175abf6d227563c16a6e74344912fd1681 Mon Sep 17 00:00:00 2001 From: zenix Date: Thu, 7 Apr 2022 19:25:02 +0900 Subject: [PATCH] feature: implement Elliott Wave Oscilla --- pkg/strategy/ewo_dgtrd/2 | 103 +++++++++++++++++++++++++++++ pkg/strategy/ewo_dgtrd/strategy.go | 102 ++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 pkg/strategy/ewo_dgtrd/2 create mode 100644 pkg/strategy/ewo_dgtrd/strategy.go diff --git a/pkg/strategy/ewo_dgtrd/2 b/pkg/strategy/ewo_dgtrd/2 new file mode 100644 index 000000000..85cee29d9 --- /dev/null +++ b/pkg/strategy/ewo_dgtrd/2 @@ -0,0 +1,103 @@ +package ewo_dgtrd + +import ( + "context" + + "github.com/sirupsen/logrus" + + "github.com/c9s/bbgo/pkg/bbgo" + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +const ID = "ewo_dgtrd" + +var log = logrus.WithField("strategy", ID) + +func init() { + bbgo.RegisterStrategy(ID, &Strategy{}) +} + +type Strategy struct { + Symbol string `json:"symbol"` + Interval types.Interval `json:"interval"` + Threshold float64 `json:"threshold"` // strength threshold + UseEma bool `json:"useEma"` // use exponential ma or simple ma + SignalWindow int `json:"sigWin"` // signal window +} + +func (s *Strategy) ID() string { + return ID +} + +func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) { + log.Infof("subscribe %s", s.Symbol) + session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval.String()}) +} + +type EwoSignal interface { + types.Series + Update(value float64) +} + +func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error { + store, ok := session.MarketDataStore(s.Symbol) + if !ok { + log.Errorf("cannot find symbol %s", s.Symbol) + return + } + klineWindow, ok := store.KLinesOfInterval(s.Interval) + if !ok { + log.Errorf("cannot find kline window of %v", s.Interval) + return + } + indicatorSet, ok := session.StandardIndicatorSet(s.Symbol) + if !ok { + log.Errorf("cannot get indicatorSet of %s", s.Symbol) + return + } + var ma5, ma34 Series + if s.UseEma { + ma5 = indicatorSet.EWMA(types.IntervalWindow{s.Interval, 5}) + ma34 = indicatorSet.EWMA(types.IntervalWindow{s.Interval, 34}) + } else { + ma5 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 5}) + ma34 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 34}) + } + var ewoSignal EwoSignal + if s.UseEma { + ewoSignal := &indicator.EWMA{types.IntervalWindow{s.Interval, s.SignalWindow}} + } else { + ewoSignal := &indicator.SMA{types.IntervalWindow{s.Interval, s.SignalWindow}} + } + for _, kline := range klineWindow { + ewoSignal.Update(kline) + } + session.MarketDataStream.OnKLineClosed(func(kline types.KLine) { + var ewo + if kline.Symbol != s.Symbol { + return + } + ewo = types.Mul(types.Minus(types.Div(ma5, ma34), 1.0), 100.) + ewoSignal.Update(ewo.Last()) + + if s.UseEma { + ewoSignal = indicator.EWMA(ewo, s.SignalWindow) + } else { + ewoSignal = sma(ewo, s.SignalWindow) + } + + if CrossOver(ewo, ewoSignal).Last() { + if ewo.Last() < -s.Threshold { + // strong long + } else { + // Long + } + } else if CrossUnder(ewo, ewoSignal).Last() { + if ewo.Last() > s.Threshold { + // Strong short + } else { + // short + } + } +} diff --git a/pkg/strategy/ewo_dgtrd/strategy.go b/pkg/strategy/ewo_dgtrd/strategy.go new file mode 100644 index 000000000..0f8cca74a --- /dev/null +++ b/pkg/strategy/ewo_dgtrd/strategy.go @@ -0,0 +1,102 @@ +package ewo_dgtrd + +import ( + "context" + + "github.com/sirupsen/logrus" + + "github.com/c9s/bbgo/pkg/bbgo" + "github.com/c9s/bbgo/pkg/indicator" + "github.com/c9s/bbgo/pkg/types" +) + +const ID = "ewo_dgtrd" + +var log = logrus.WithField("strategy", ID) + +func init() { + bbgo.RegisterStrategy(ID, &Strategy{}) +} + +type Strategy struct { + Symbol string `json:"symbol"` + Interval types.Interval `json:"interval"` + Threshold float64 `json:"threshold"` // strength threshold + UseEma bool `json:"useEma"` // use exponential ma or simple ma + SignalWindow int `json:"sigWin"` // signal window +} + +func (s *Strategy) ID() string { + return ID +} + +func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) { + log.Infof("subscribe %s", s.Symbol) + session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval.String()}) +} + +type EwoSignal interface { + types.Series + Update(value float64) +} + +func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession) error { + indicatorSet, ok := session.StandardIndicatorSet(s.Symbol) + if !ok { + log.Errorf("cannot get indicatorSet of %s", s.Symbol) + return nil + } + var ma5, ma34, ewo types.Series + if s.UseEma { + ma5 = indicatorSet.EWMA(types.IntervalWindow{s.Interval, 5}) + ma34 = indicatorSet.EWMA(types.IntervalWindow{s.Interval, 34}) + } else { + ma5 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 5}) + ma34 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 34}) + } + ewo = types.Mul(types.Minus(types.Div(ma5, ma34), 1.0), 100.) + var ewoSignal EwoSignal + if s.UseEma { + ewoSignal = &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}} + } else { + ewoSignal = &indicator.SMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}} + } + session.MarketDataStream.OnKLineClosed(func(kline types.KLine) { + if kline.Symbol != s.Symbol { + return + } + if ewoSignal.Length() == 0 { + // lazy init + ewoVals := types.ToReverseArray(ewo) + for _, ewoValue := range ewoVals { + ewoSignal.Update(ewoValue) + } + } else { + ewoSignal.Update(ewo.Last()) + } + + lastPrice, ok := session.LastPrice(s.Symbol) + if !ok { + return + } + + if types.CrossOver(ewo, ewoSignal).Last() { + if ewo.Last() < -s.Threshold { + // strong long + log.Infof("strong long at %v", lastPrice) + } else { + log.Infof("long at %v", lastPrice) + // Long + } + } else if types.CrossUnder(ewo, ewoSignal).Last() { + if ewo.Last() > s.Threshold { + // Strong short + log.Infof("strong short at %v", lastPrice) + } else { + // short + log.Infof("short at %v", lastPrice) + } + } + }) + return nil +}