package trend import ( "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/strat" "github.com/banbox/banstagy/com" ta "github.com/banbox/banta" "math" ) /* 自研均线趋势策略,多空都开。适合1h 尝试加止损、止盈、限价入场,都会导致整体收益减少 48个品种,21年到24年11月1日,累计收益1291%,最大回撤66%,夏普9.2 总收益90%来自多头 但加入空投可使夏普从5.64提高到9.2 TODO 添加相似矩阵过滤,趋同的品种不开单 结合MACDEMA,优化做多逻辑 相比SMACrossV1,略微不平滑;且收益降低;优先使用SMACrossV1 */ type maSta struct { openPrice float64 bigRsi float64 bigLong bool } func SMACrossV2(p *config.RunPolicyConfig) *strat.TradeStrat { // 初始:两个SMA 做空:SMA 15 8 maLType := com.DefMaType(p, "maL", com.MaSMA) maSType := com.DefMaType(p, "maS", com.MaSMA) maLLen := p.DefInt("maLLen", 20, core.PNormF(7, 70, 20, 0)) maSLen := p.DefInt("maSLen", 15, core.PNormF(5, 50, 15, 0)) maLRate := p.Def("maLRate", 9, core.PNorm(2, 15)) maSRate := p.Def("maSRate", 8, core.PNorm(2, 14)) maLBig := int(math.Round(float64(maLLen) * maLRate)) maSBig := int(math.Round(float64(maSLen) * maSRate)) p.KeepHyperOnly("maSLen", "maSRate") return &strat.TradeStrat{ WarmupNum: 200, EachMaxShort: 1, EachMaxLong: 1, BatchInOut: true, OnStartUp: func(s *strat.StratJob) { s.More = &maSta{} }, OnPairInfos: func(s *strat.StratJob) []*strat.PairSub { return []*strat.PairSub{ {"_cur_", "4h", 30}, } }, OnBar: func(s *strat.StratJob) { e := s.Env m, _ := s.More.(*maSta) c := e.Close.Get(0) atr := ta.ATR(e.High, e.Low, e.Close, 30) openRate := 0.015 * c / atr.Get(0) if openRate < 1 { openRate = math.Sqrt(openRate) } er := ta.ER(e.Close, 50) erDelta := min(1.3, er.Get(0)/er.Get(12)) if erDelta > 1 { openRate *= erDelta } else { // 过滤掉ER下降的 openRate = 0 } // 方案一:多空SMA:略微平滑,收益略小一点 // 方案二:多空VWMA:略微波动大点,收益略好一点 price := ta.AvgPrice(e) ma1 := com.GetMA(price, e.Volume, maLType, maLLen) ma2 := com.GetMA(price, e.Volume, maLType, maLBig) x1 := ma1.Cross(ma2) if x1 == 1 && openRate > 0 { s.OpenOrder(&strat.EnterReq{Tag: "long", CostRate: openRate, Log: true}) } else if x1 == -1 { s.CloseOrders(&strat.ExitReq{Tag: "xlong", Dirt: core.OdDirtLong, Log: true}) } if len(s.LongOrders) > 0 && m.openPrice < c && m.bigRsi > 80 && ma1.Get(0)-ma1.Get(2) > atr.Get(0)*0.5 { // 多仓,盈利,小均线变向平仓 m.bigLong = true } //if m.bigLong && m.bigRsi > 70 { // m.bigLong = false // s.OpenOrder(&strat.EnterReq{Tag: "short2", Short: true, CostRate: openRate, Log: true}) //} ma3 := com.GetMA(price, e.Volume, maSType, maSLen) ma4 := com.GetMA(price, e.Volume, maSType, maSBig) x2 := ma3.Cross(ma4) if len(s.ShortOrders) == 0 && x2 == -1 && openRate > 0 { m.bigLong = false s.OpenOrder(&strat.EnterReq{Tag: "short", Short: true, CostRate: openRate, Log: true}) } else if x2 == 1 { s.CloseOrders(&strat.ExitReq{Tag: "xshort", Dirt: core.OdDirtShort, Log: true}) } if len(s.ShortOrders) > 0 && m.bigRsi < 30 && s.ShortOrders[0].ProfitRate > 0 { s.CloseOrders(&strat.ExitReq{Tag: "xshort2", Dirt: core.OdDirtShort, Log: true}) } //if !s.IsWarmUp { // line := fmt.Sprintf("%s %s %d %d %f %f %f %f %f atr:%f er:%f m1:%f m2:%f m3:%f m4:%f x1:%v x2:%v rate:%f", // s.Account, s.Symbol.Symbol, len(s.LongOrders), len(s.ShortOrders), // e.Open.Get(0), e.High.Get(0), e.Low.Get(0), e.Close.Get(0), e.Volume.Get(0), // atr.Get(0), er.Get(0), ma1.Get(0), ma2.Get(0), ma3.Get(0), ma4.Get(0), // x1, x2, openRate) // s.Strat.WriteOutput(line, true) //} }, OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) { m, _ := s.More.(*maSta) m.bigRsi = ta.RSI(e.Close, 9).Get(0) }, OnBatchJobs: func(jobs []*strat.StratJob) { longNum := float64(0) bigDown := float64(0) for _, s := range jobs { if len(s.LongOrders) == 0 { continue } longNum += 1 m, _ := s.More.(*maSta) if m.bigLong { bigDown += 1 } } if bigDown*1.5 > longNum && longNum*2 > float64(len(jobs)) { for _, s := range jobs { if len(s.LongOrders) == 0 { continue } s.CloseOrders(&strat.ExitReq{Tag: "xlong2", Dirt: core.OdDirtLong, Log: true}) } } }, } }