// 修复后的banbot完整策略代码 package llmjob import ( "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/strat" ta "github.com/banbox/banta" "math" ) // State4513 存储每个品种的策略状态 type State4513 struct { dir int // 当前趋势方向,1=上升趋势,-1=下降趋势 longStop float64 // 多头止损价格 shortStop float64 // 空头止损价格 longStopPrev float64 // 前一个多头止损价格 shortStopPrev float64 // 前一个空头止损价格 trailingStop float64 // 当前跟踪止损价格 } // Demo 创建并返回策略实例 func J4513(pol *config.RunPolicyConfig) *strat.TradeStrat { // 定义可调超参数 bbLength := pol.DefInt("BBLength", 100, core.PNorm(20, 200)) stdDev := pol.Def("StdDev", 10, core.PNorm(1, 20)) useMovingAverage := pol.Def("useMovingAverage", 1, core.PUniform(0, 1)) // 1=true, 0=false maType := pol.DefInt("maType", 3, core.PUniform(0, 5)) // 0=ema, 1=sma, 2=hma, 3=rma, 4=vwma, 5=wma lookbackPeriod := pol.DefInt("lookbackPeriod", 22, core.PNorm(5, 50)) atrMaType := pol.DefInt("atrMaType", 2, core.PUniform(0, 5)) // 0=ema, 1=sma, 2=hma, 3=rma, 4=vwma, 5=wma atrLength := pol.DefInt("atrLength", 10, core.PNorm(5, 30)) atrMult := pol.Def("atrMult", 4.2, core.PNorm(1, 10)) wicks := pol.Def("wicks", 0, core.PUniform(0, 1)) // 1=true, 0=false considerYearlyHighLow := pol.Def("considerYearlyHighLow", 0, core.PUniform(0, 1)) // 1=true, 0=false considerNewLongTermHighLows := pol.Def("considerNewLongTermHighLows", 0, core.PUniform(0, 1)) // 1=true, 0=false tradeDirection := pol.DefInt("tradeDirection", 0, core.PUniform(0, 2)) // 0=all, 1=long, 2=short // 固定参数 shortHighLowPeriod := 100 longHighLowPeriod := 200 return &strat.TradeStrat{ Name: "Bollinger Percentage Bands", WarmupNum: int(math.Max(float64(bbLength), float64(longHighLowPeriod))) + 10, HedgeOff: true, // 单向持仓 EachMaxLong: 1, EachMaxShort: 1, RunTimeFrames: []string{"8h"}, OnStartUp: func(s *strat.StratJob) { // 初始化策略状态 state := &State4513{ dir: 1, longStop: 0, shortStop: 0, } s.More = state }, OnBar: func(s *strat.StratJob) { e := s.Env state, _ := s.More.(*State4513) // 1. 计算布林带指标 // 修复错误1:布林带计算参数错误 // 原始策略使用bb(close, BBLength, StdDev),其中StdDev是标准差倍数 // 修改为使用正确的参数格式,提供上下轨的标准差倍数 upperClose, _, lowerClose := ta.BBANDS(e.Close, bbLength, stdDev, stdDev) upperHigh, _, lowerHigh := ta.BBANDS(e.High, bbLength, stdDev, stdDev) upperLow, _, lowerLow := ta.BBANDS(e.Low, bbLength, stdDev, stdDev) // 2. 计算布林带百分比 percentBClose := e.Close.Sub(lowerClose).Div(upperClose.Sub(lowerClose)).Mul(100) percentBHigh := e.High.Sub(lowerHigh).Div(upperHigh.Sub(lowerHigh)).Mul(100) percentBLow := e.Low.Sub(lowerLow).Div(upperLow.Sub(lowerLow)).Mul(100) // 3. 应用移动平均线平滑 var newClose, newHigh, newLow *ta.Series if useMovingAverage > 0.5 { percentBMAClose := getMovingAverage(percentBClose, maType, lookbackPeriod) percentBMAHigh := getMovingAverage(percentBHigh, maType, lookbackPeriod) percentBMALow := getMovingAverage(percentBLow, maType, lookbackPeriod) newClose = percentBMAClose newHigh = percentBMAHigh newLow = percentBMALow } else { newClose = percentBClose newHigh = percentBHigh newLow = percentBLow } // 4. 计算ATR和止损 closePrev := newClose.Back(1) highPrev := newHigh.Back(1) lowPrev := newLow.Back(1) trueRange := newHigh.Max(closePrev).Sub(newLow.Min(closePrev)) averageTrueRange := getMovingAverage(trueRange, atrMaType, atrLength) atr := averageTrueRange.Mul(atrMult) longStopVal := newClose.Sub(atr) shortStopVal := newClose.Add(atr) // 修复错误2:止损更新逻辑不完整 // 原始策略使用nz(longStop[1], longStop)处理前一个止损值不存在的情况 // 修改为正确处理初始状态,并确保止损只能向有利方向移动 if state.longStopPrev == 0 { state.longStopPrev = longStopVal.Get(0) } if state.shortStopPrev == 0 { state.shortStopPrev = shortStopVal.Get(0) } // 修复错误3:使用Series运算创建常量而不是ta.Const if wicks > 0.5 { if lowPrev.Get(0) > state.longStopPrev { // 使用Max确保止损只能向有利方向移动 longStopVal = longStopVal.Max(newClose.Sub(newClose).Add(state.longStopPrev)) } } else { if closePrev.Get(0) > state.longStopPrev { longStopVal = longStopVal.Max(newClose.Sub(newClose).Add(state.longStopPrev)) } } if wicks > 0.5 { if highPrev.Get(0) < state.shortStopPrev { shortStopVal = shortStopVal.Min(newClose.Sub(newClose).Add(state.shortStopPrev)) } } else { if closePrev.Get(0) < state.shortStopPrev { shortStopVal = shortStopVal.Min(newClose.Sub(newClose).Add(state.shortStopPrev)) } } // 5. 判断趋势方向 // 修复错误4:趋势方向判断可能存在边界条件处理错误 // 原始策略使用三元运算符,逻辑清晰且紧凑 // 修改为更接近原始策略的实现 if state.dir == -1 { if wicks > 0.5 { if newHigh.Get(0) > state.shortStopPrev { state.dir = 1 } } else { if newClose.Get(0) > state.shortStopPrev { state.dir = 1 } } } else if state.dir == 1 { if wicks > 0.5 { if newLow.Get(0) < state.longStopPrev { state.dir = -1 } } else { if newClose.Get(0) < state.longStopPrev { state.dir = -1 } } } if state.dir == 1 { state.trailingStop = longStopVal.Get(0) } else { state.trailingStop = shortStopVal.Get(0) } // 6. 判断辅助条件 yearlyHighCondition, yearlyLowCondition := getYearlyHighLowCondition(e, considerYearlyHighLow > 0.5) newHighS, newLowS := calculateNewHighLows(e, shortHighLowPeriod, longHighLowPeriod, considerNewLongTermHighLows > 0.5) // 7. 生成交易信号 buyCondition := state.dir == 1 && yearlyHighCondition && newHighS exitBuyCondition := state.dir == -1 sellCondition := state.dir == -1 && yearlyLowCondition && newLowS exitSellCondition := state.dir == 1 // 8. 执行交易 if buyCondition && (tradeDirection == 0 || tradeDirection == 1) { s.OpenOrder(&strat.EnterReq{ Tag: "Buy", Short: false, }) } if exitBuyCondition && len(s.LongOrders) > 0 { s.CloseOrders(&strat.ExitReq{ Tag: "ExitBuy", Dirt: core.OdDirtLong, }) } if sellCondition && (tradeDirection == 0 || tradeDirection == 2) { s.OpenOrder(&strat.EnterReq{ Tag: "Sell", Short: true, }) } if exitSellCondition && len(s.ShortOrders) > 0 { s.CloseOrders(&strat.ExitReq{ Tag: "ExitSell", Dirt: core.OdDirtShort, }) } // 更新状态 state.longStop = longStopVal.Get(0) state.shortStop = shortStopVal.Get(0) state.longStopPrev = state.longStop state.shortStopPrev = state.shortStop }, } } // getMovingAverage 根据类型获取移动平均线 func getMovingAverage(source *ta.Series, maType int, length int) *ta.Series { switch maType { case 0: // ema return ta.EMA(source, length) case 1: // sma return ta.SMA(source, length) case 2: // hma return ta.HMA(source, length) case 3: // rma return ta.RMA(source, length) case 4: // vwma - 在Banbot中可能没有直接对应,使用SMA代替 return ta.SMA(source, length) case 5: // wma return ta.WMA(source, length) default: return ta.SMA(source, length) } } // calculateNewHighLows 计算新的高低点条件 func calculateNewHighLows(e *ta.BarEnv, shortPeriod, longPeriod int, consider bool) (bool, bool) { shortHigh := ta.Highest(e.High, shortPeriod) longHigh := ta.Highest(e.High, longPeriod) shortLow := ta.Lowest(e.Low, shortPeriod) longLow := ta.Lowest(e.Low, longPeriod) newHigh := true newLow := true if consider { newHigh = math.Abs(shortHigh.Get(0)-longHigh.Get(0)) < 1e-10 newLow = math.Abs(shortLow.Get(0)-longLow.Get(0)) < 1e-10 } return newHigh, newLow } // getYearlyHighLowCondition 获取年度高低点条件 func getYearlyHighLowCondition(e *ta.BarEnv, consider bool) (bool, bool) { if !consider { return true, true } // 在Banbot中,我们无法直接获取年度数据,所以简化处理 // 使用较长期的高低点来近似年度高低点 yearlyHigh := ta.Highest(e.High, 365) yearlyLow := ta.Lowest(e.Low, 365) yearlyHighCondition := e.Close.Get(0) > yearlyHigh.Get(0) yearlyLowCondition := e.Close.Get(0) < yearlyLow.Get(0) return yearlyHighCondition, yearlyLowCondition }