package llmjob import ( "fmt" "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/strat" "github.com/banbox/banexg/utils" ta "github.com/banbox/banta" "math" ) /* 需要多品种综合信息,品种数不得少于30 做多:RSI超卖、RSI轻度超买跟随 做空:全市场超卖动能枯竭做空 */ // State845 存储每个品种的RSI动态区间策略状态 type State845 struct { position int // 当前持仓状态: 0=无持仓, 1=多头, -1=空头 overBoughtDyn float64 // 动态超买水平 overSoldDyn float64 // 动态超卖水平 rsi float64 maSub float64 atr float64 } // RsiDynamic RSI动态区间反转策略 func J845(pol *config.RunPolicyConfig) *strat.TradeStrat { // 定义可调超参数 rsiPeriod := pol.DefInt("rsiPeriod", 12, core.PNorm(7, 21)) overBought := pol.Def("overBought", 76, core.PNorm(70, 90)) overSold := pol.Def("overSold", 26, core.PNorm(20, 40)) sensitivity := pol.Def("sensitivity", 2.5, core.PNorm(1.0, 5.0)) openRate, closeRate := float64(1), float64(0) prevRsiAvg, prevRsiBig, rsiDelta := float64(0), float64(0), float64(0) prevOverBuy, prevStamp, prevOpenRate := 0, int64(0), float64(0) stopLongUntil := int64(0) dayMS := int64(utils.TFToSecs("1d") * 1000) return &strat.TradeStrat{ WarmupNum: 91, // 预热K线数量 EachMaxLong: 1, // 最多开1个多单 EachMaxShort: 1, // 最多开1个空单 RunTimeFrames: []string{"1d"}, BatchInOut: true, OnStartUp: func(s *strat.StratJob) { // 初始化每个品种的状态 s.More = &State845{ position: 0, overBoughtDyn: overBought, overSoldDyn: overSold, } }, OnBar: func(s *strat.StratJob) { e := s.Env state, _ := s.More.(*State845) rsi := ta.RSI(e.Close, rsiPeriod) rsiValue := rsi.Get(0) state.rsi = rsiValue state.atr = ta.ATR(e.High, e.Low, e.Close, 14).Get(0) ma20 := ta.SMA(e.Close, 20).Get(0) ma90 := ta.SMA(e.Close, 90).Get(0) state.maSub = ma20 - ma90 // 计算动态超买超卖水平 calculateDynamicLevels(rsi, sensitivity, overBought, overSold, state) // 生成交易信号 if state.position == 0 && openRate > 0 && e.TimeStop >= stopLongUntil { // 无持仓 if rsiValue < state.overSoldDyn { // RSI跌破超卖水平,建立多头仓位 s.OpenOrder(&strat.EnterReq{ Tag: "long", CostRate: openRate, Log: true, }) state.position = 1 } } else if state.position == -1 { // 持有空头仓位 if rsiValue <= state.overBoughtDyn { // RSI从超买区域回落,平掉多头仓位 s.CloseOrders(&strat.ExitReq{ Tag: "exit2", Dirt: core.OdDirtLong, EnterTag: "long2", Log: true, }) state.position = 0 } } else if state.position == 1 { // 持有多头仓位 if rsiValue >= state.overSoldDyn { // RSI从超卖区域回升,平掉多头仓位 s.CloseOrders(&strat.ExitReq{ Tag: "exit1", Dirt: core.OdDirtLong, EnterTag: "long", Log: true, }) state.position = 0 } } }, OnBatchJobs: func(jobs []*strat.StratJob) { rsiAvg, rsiBig := float64(0), float64(0) overBuyNum, curStamp := 0, int64(0) var st *strat.TradeStrat for _, s := range jobs { state, _ := s.More.(*State845) rsiAvg += state.rsi if state.rsi >= 68 { rsiBig += 1 } if state.rsi > state.overBoughtDyn { overBuyNum += 1 } curStamp = s.Env.TimeStop st = s.Strat } numFlt := float64(len(jobs)) rsiAvg /= numFlt rsiBig /= numFlt if prevRsiAvg >= 70 && rsiAvg < prevRsiAvg && prevRsiBig > 0.5 { // 极端过热转向,平仓,禁止做多2周 openRate, closeRate = -2, 1 if prevRsiBig > 0.65 && prevOpenRate < 0 { openRate = -3 stopLongUntil = curStamp + dayMS*14 } } else if rsiAvg > 77 && rsiBig > 0.75 || rsiAvg > 70 && rsiBig > 0.5 && (rsiAvg-prevRsiAvg)*5 < rsiDelta { // 严重过热,平仓一半,禁止开仓 openRate, closeRate = -1, 0.5 if curStamp < stopLongUntil { stopLongUntil = max(stopLongUntil, curStamp+dayMS*10) } } else if rsiAvg > 70 && rsiBig > 0.5 { // 中度过热 openRate, closeRate = 0.6, 0 if curStamp < stopLongUntil { stopLongUntil = max(stopLongUntil, curStamp+dayMS*6) } } else if rsiAvg > 60 && rsiBig > 0.3 { // 轻度过热 openRate, closeRate = 0.8, 0 } else { openRate, closeRate = 1, 0 } if closeRate > 0 { for _, s := range jobs { if s.OrderNum > 0 { s.CloseOrders(&strat.ExitReq{ Tag: "cut", Dirt: core.OdDirtLong, ExitRate: closeRate, Log: true, }) } } } if overBuyNum >= 2 || overBuyNum+prevOverBuy >= 3 { // 超买跟随,仅适用于多数品种一同上涨,所以要限制数量 for _, s := range jobs { state, _ := s.More.(*State845) if state.rsi > state.overBoughtDyn { // RSI突破超买水平,追涨买入 s.OpenOrder(&strat.EnterReq{ Tag: "long2", CostRate: openRate, Log: true, }) state.position = -1 } } } // 超买做空机制,仅对特定品种表现良好,对于动态品种表现更差 //if openRate <= -3 { // for _, s := range jobs { // state, _ := s.More.(*State845) // if state.maSub < 0 || state.rsi < 76 || len(s.ShortOrders) > 0 { // continue // } // // 牛市中全市场超买时做空;不可在ObBatchJobs中开单,会变差 // s.OpenOrder(&strat.EnterReq{ // Tag: "short1", // //Short: true, // TakeProfitVal: state.atr * 2, // StopLossVal: state.atr * 2, // Log: true, // }) // } //} if openRate < 1 { st.WriteOutput(fmt.Sprintf("num: %d, rate: %.3f, rsi: %.3f, rsiBig: %.3f", len(jobs), openRate, rsiAvg, rsiBig), true) } if curStamp > prevStamp { rsiDelta = rsiAvg - prevRsiAvg prevRsiAvg = rsiAvg prevRsiBig = rsiBig prevStamp = curStamp prevOpenRate = openRate prevOverBuy = overBuyNum } }, } } // calculateDynamicLevels 计算动态超买超卖水平 func calculateDynamicLevels(rsi *ta.Series, sensitivity, overBought, overSold float64, state *State845) { // 计算近期RSI波动率(标准差) stdDev, _ := ta.StdDevBy(rsi, 14, 1) rsiVolatility := stdDev.Get(0) // 计算动态调整因子 adjustmentFactor := sensitivity * rsiVolatility / 10 // 计算动态超买超卖水平 state.overBoughtDyn = overBought + adjustmentFactor*(100-overBought)/100 state.overSoldDyn = overSold - adjustmentFactor*overSold/100 // 确保动态水平在合理范围内 state.overBoughtDyn = math.Max(70, math.Min(95, state.overBoughtDyn)) state.overSoldDyn = math.Max(5, math.Min(40, state.overSoldDyn)) }