package rsicross import ( "context" "fmt" "sync" "git.qtrade.icu/lychiyu/qbtrade/pkg/fixedpoint" indicatorv2 "git.qtrade.icu/lychiyu/qbtrade/pkg/indicator/v2" "git.qtrade.icu/lychiyu/qbtrade/pkg/qbtrade" "git.qtrade.icu/lychiyu/qbtrade/pkg/strategy/common" "git.qtrade.icu/lychiyu/qbtrade/pkg/types" "git.qtrade.icu/lychiyu/qbtrade/pkg/util" ) const ID = "rsicross" func init() { qbtrade.RegisterStrategy(ID, &Strategy{}) } type Strategy struct { *common.Strategy Environment *qbtrade.Environment Market types.Market Symbol string `json:"symbol"` Interval types.Interval `json:"interval"` SlowWindow int `json:"slowWindow"` FastWindow int `json:"fastWindow"` OpenBelow fixedpoint.Value `json:"openBelow"` CloseAbove fixedpoint.Value `json:"closeAbove"` qbtrade.OpenPositionOptions } func (s *Strategy) Initialize() error { if s.Strategy == nil { s.Strategy = &common.Strategy{} } return nil } func (s *Strategy) ID() string { return ID } func (s *Strategy) InstanceID() string { return fmt.Sprintf("%s:%s:%s:%d-%d", ID, s.Symbol, s.Interval, s.FastWindow, s.SlowWindow) } func (s *Strategy) Subscribe(session *qbtrade.ExchangeSession) { session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval}) } func (s *Strategy) Run(ctx context.Context, orderExecutor qbtrade.OrderExecutor, session *qbtrade.ExchangeSession) error { s.Strategy.Initialize(ctx, s.Environment, session, s.Market, ID, s.InstanceID()) fastRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.FastWindow}) slowRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.SlowWindow}) rsiCross := indicatorv2.Cross(fastRsi, slowRsi) rsiCross.OnUpdate(func(v float64) { switch indicatorv2.CrossType(v) { case indicatorv2.CrossOver: if s.OpenBelow.Sign() > 0 && fastRsi.Last(0) > s.OpenBelow.Float64() { return } opts := s.OpenPositionOptions opts.Long = true if price, ok := session.LastPrice(s.Symbol); ok { opts.Price = price } // opts.Price = closePrice opts.Tags = []string{"rsiCrossOver"} if _, err := s.OrderExecutor.OpenPosition(ctx, opts); err != nil { util.LogErr(err, "unable to open position") } case indicatorv2.CrossUnder: if s.CloseAbove.Sign() > 0 && fastRsi.Last(0) < s.CloseAbove.Float64() { return } if err := s.OrderExecutor.ClosePosition(ctx, fixedpoint.One); err != nil { util.LogErr(err, "failed to close position") } } }) qbtrade.OnShutdown(ctx, func(ctx context.Context, wg *sync.WaitGroup) { defer wg.Done() qbtrade.Sync(ctx, s) }) return nil }