package surge import ( "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/strat" ta "github.com/banbox/banta" "math" "sort" ) /* 全市场瞬时暴跌做空策略。适用于十分钟跌去70%的情况。 可使用下面脚本回测:surge/down_test.go E:\trade\go\bandata\backtest\bdb35c1d91\surge_alldown.log */ type AllDownSta struct { volBase float64 // 平均成交量 dirtRate float64 // K线方向倍率,范围约:[-3,3] barRate float64 // K线比率 volRate float64 // 成交量倍率 } func AllDown(p *config.RunPolicyConfig) *strat.TradeStrat { //var allowLog = false lenAtr, lenAtrS := 20, 30 baseAtrLen := int(math.Round(float64(lenAtr) * 4.3)) baseAtrLenS := baseAtrLen * 3 / 2 env, _ := ta.NewBarEnv("", "", "", "1m") lenS := env.NewSeries(nil) volS := env.NewSeries(nil) var prevLen float64 var volThres = 2.8 var openDirt float64 return &strat.TradeStrat{ WarmupNum: 150, BatchInOut: true, TimeFrames: "1m", OnStartUp: func(s *strat.StratJob) { s.More = &AllDownSta{} }, OnPairInfos: func(s *strat.StratJob) []*strat.PairSub { return []*strat.PairSub{ {"_cur_", "15m", 100}, } }, OnBar: func(s *strat.StratJob) { e := s.Env m, _ := s.More.(*AllDownSta) atr := ta.ATR(e.High, e.Low, e.Close, lenAtrS) atrBase := ta.Lowest(ta.Highest(atr, lenAtrS), baseAtrLenS).Get(0) o, c := e.Open.Get(0), e.Close.Get(0) h, l := e.High.Get(0), e.Low.Get(0) m.dirtRate = (c - o) / atrBase m.barRate = (c - o) / (h - l) m.volRate = ta.EMA(e.Volume, lenAtr).Get(0) / m.volBase }, OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) { m, _ := s.More.(*AllDownSta) maVol := ta.EMA(e.Volume, lenAtr) m.volBase = ta.Lowest(ta.Highest(maVol, lenAtr), baseAtrLen).Get(0) / 3 }, OnBatchJobs: func(jobs []*strat.StratJob) { jobNum := len(jobs) volRates := make([]float64, 0, jobNum) lenRates := make([]float64, 0, jobNum) solidRates := make([]float64, 0, jobNum) volIds := make([]*core.Int64Flt, 0, jobNum) var timeMS int64 for i, j := range jobs { m, _ := j.More.(*AllDownSta) volRates = append(volRates, m.volRate) lenRates = append(lenRates, m.dirtRate) solidRates = append(solidRates, m.barRate) timeMS = max(timeMS, j.Env.TimeStop) volIds = append(volIds, &core.Int64Flt{ Int: int64(i), Val: m.volRate, }) } sort.Float64s(volRates) sort.Float64s(lenRates) sort.Float64s(solidRates) vol := volRates[jobNum/2] solid := solidRates[jobNum/2] lenMid := lenRates[jobNum/2] //len33 := lenRates[jobNum/3] len66 := lenRates[jobNum*2/3] val := lenMid + vol env.TimeStop = max(timeMS, env.TimeStop) if math.IsNaN(val) || math.IsInf(val, 0) || lenS.Cached() { return } lenS.Append(lenMid) volS.Append(vol) dataLen := volS.Len() if dataLen > env.MaxCache*2 { volS.Cut(env.MaxCache) lenS.Cut(env.MaxCache) } volMa1 := ta.SMA(volS, 3).Get(1) low5len := ta.Lowest(lenS, 30).Get(1) if openDirt >= -1 && vol > volThres && lenMid < low5len && len66 < 0 && solid < -0.5 && prevLen < 0 { // 成交量突破阈值,全市场大阴线,前一个也是阴线 prevRate := lenMid / prevLen if prevRate < 3 && prevRate > 1.5 && vol/volMa1 < 2 { // 与前一个k线比率不超过3,成交量缓慢增长 openDirt -= 1 costRate := 2.0 if vol < 4 { costRate *= vol / 4 } sort.Slice(volIds, func(i, j int) bool { return volIds[i].Val < volIds[j].Val }) // 做空成交量最大的1/3部分,获利最丰(收益45%, 55%, 66%) items := volIds[jobNum*2/3:] for _, it := range items { jobs[it.Int].OpenOrder(&strat.EnterReq{ Tag: "short", CostRate: costRate, Short: true, }) } } } if openDirt < 0 { // 正在做空/多 if lenMid*openDirt < 0 { openDirt = 0 for _, job := range jobs { job.CloseOrders(&strat.ExitReq{ Tag: "exit", Force: true, }) } } } prevLen = lenMid //if !allowLog && vol > 2.5 { // allowLog = true //} //if !math.IsNaN(vol) { // jobs[0].Strat.WriteOutput(fmt.Sprintf("%.2f %.2f %.2f %.2f %.2f", vol, solid, lenMid, len33, len66), true) // if vol < 1.4 { // allowLog = false // } //} }, } }