package llmjob import ( "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/strat" ta "github.com/banbox/banta" "math" ) // State5609 存储每个品种的策略状态 type State5609 struct { longEntryPrice float64 shortEntryPrice float64 longEntryBar int64 shortEntryBar int64 } func J5609(pol *config.RunPolicyConfig) *strat.TradeStrat { // 定义可调超参数 atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 30)) bbLength := pol.DefInt("bbLength", 18, core.PNorm(10, 50)) bbStdDev := pol.Def("bbStdDev", 1.9, core.PNorm(1, 3)) rsiPeriod := pol.DefInt("rsiPeriod", 12, core.PNorm(7, 21)) atrMultiplier := pol.Def("atrMultiplier", 1.6, core.PNorm(0.5, 3)) vwapLength := pol.DefInt("vwapLength", 20, core.PNorm(10, 40)) targetMultiplier := pol.Def("targetMultiplier", 2.4, core.PNorm(1, 4)) maxHoldingBars := pol.DefInt("maxHoldingBars", 3, core.PNorm(2, 10)) // 计算预热期,确保所有指标都有足够的数据计算 warmupNum := bbLength if atrPeriod > warmupNum { warmupNum = atrPeriod } if rsiPeriod > warmupNum { warmupNum = rsiPeriod } if vwapLength > warmupNum { warmupNum = vwapLength } // 额外增加20个K线确保稳定性 warmupNum += 20 return &strat.TradeStrat{ Name: "OptionsStrategy", WarmupNum: warmupNum, HedgeOff: true, // 单向持仓模式 EachMaxLong: 1, // 最多一个多头订单 EachMaxShort: 1, // 最多一个空头订单 RunTimeFrames: []string{"4h"}, OnStartUp: func(s *strat.StratJob) { // 初始化每个品种的策略状态 s.More = &State5609{ longEntryPrice: 0, shortEntryPrice: 0, longEntryBar: 0, shortEntryBar: 0, } }, OnBar: func(s *strat.StratJob) { e := s.Env state := s.More.(*State5609) // 计算技术指标 atrValue := ta.ATR(e.High, e.Low, e.Close, atrPeriod) smaValue := ta.SMA(e.Close, bbLength) stdDev := ta.StdDev(e.Close, bbLength) upperBB := smaValue.Add(stdDev.Mul(bbStdDev)) lowerBB := smaValue.Sub(stdDev.Mul(bbStdDev)) rsiValue := ta.RSI(e.Close, rsiPeriod) // 修复错误1:使用正确的VWAP计算方法 vwap := ta.Sum(e.Close.Mul(e.Volume), vwapLength).Div(ta.Sum(e.Volume, vwapLength)) volSMA := ta.SMA(e.Volume, 10) atrSMA := ta.SMA(atrValue, 20) // 获取当前值 closePrice := e.Close.Get(0) atrVal := atrValue.Get(0) upperBBVal := upperBB.Get(0) lowerBBVal := lowerBB.Get(0) rsiVal := rsiValue.Get(0) vwapVal := vwap.Get(0) volumeVal := e.Volume.Get(0) volSMAVal := volSMA.Get(0) atrSMAVal := atrSMA.Get(0) // 检查数据有效性 if math.IsNaN(closePrice) || math.IsNaN(atrVal) || math.IsNaN(upperBBVal) || math.IsNaN(lowerBBVal) || math.IsNaN(rsiVal) || math.IsNaN(vwapVal) || math.IsNaN(volumeVal) || math.IsNaN(volSMAVal) || math.IsNaN(atrSMAVal) { return } // 计算条件 volSpike := volumeVal > volSMAVal*1.5 atrFilter := atrVal > atrSMAVal*0.5 // 生成交易信号 longCE := e.Close.Cross(upperBB) == 1 && rsiVal > 60 && volSpike && closePrice > vwapVal && atrFilter longPE := e.Close.Cross(lowerBB) == -1 && rsiVal < 40 && volSpike && closePrice < vwapVal && atrFilter // 计算止损止盈 longStopLoss := 0.0 longTarget := 0.0 shortStopLoss := 0.0 shortTarget := 0.0 // 修复错误5:使用实际成交的平均价格作为止损止盈的计算基准 if len(s.LongOrders) > 0 { // 获取多头持仓的平均价格 longAvgPrice := state.longEntryPrice if longAvgPrice > 0 { longStopLoss = longAvgPrice - atrMultiplier*atrVal longTarget = longAvgPrice + targetMultiplier*atrVal } } if len(s.ShortOrders) > 0 { // 获取空头持仓的平均价格 shortAvgPrice := state.shortEntryPrice if shortAvgPrice > 0 { shortStopLoss = shortAvgPrice + atrMultiplier*atrVal shortTarget = shortAvgPrice - targetMultiplier*atrVal } } // 执行交易 if longCE && len(s.LongOrders) == 0 { s.OpenOrder(&strat.EnterReq{ Tag: "CE Entry", OrderType: core.OrderTypeMarket, StopLossVal: atrMultiplier * atrVal, TakeProfitVal: targetMultiplier * atrVal, }) // 记录入场价格和K线时间 state.longEntryPrice = closePrice state.longEntryBar = e.TimeStop } if longPE && len(s.ShortOrders) == 0 { s.OpenOrder(&strat.EnterReq{ Tag: "PE Entry", Short: true, OrderType: core.OrderTypeMarket, StopLossVal: atrMultiplier * atrVal, TakeProfitVal: targetMultiplier * atrVal, }) // 记录入场价格和K线时间 state.shortEntryPrice = closePrice state.shortEntryBar = e.TimeStop } // 出场管理 if len(s.LongOrders) > 0 { // 计算持仓时间(K线数) holdBars := e.BarCount(state.longEntryBar) // 止盈出场 if closePrice >= longTarget { s.CloseOrders(&strat.ExitReq{ Tag: "CE Target Hit", Dirt: core.OdDirtLong, Force: true, }) } // 止损出场 if closePrice <= longStopLoss { s.CloseOrders(&strat.ExitReq{ Tag: "CE Stop Loss Hit", Dirt: core.OdDirtLong, Force: true, }) } // 时间出场 if int64(holdBars) >= int64(maxHoldingBars) { s.CloseOrders(&strat.ExitReq{ Tag: "CE Timed Exit", Dirt: core.OdDirtLong, Force: true, }) } } if len(s.ShortOrders) > 0 { // 计算持仓时间(K线数) holdBars := e.BarCount(state.shortEntryBar) // 止盈出场 if closePrice <= shortTarget { s.CloseOrders(&strat.ExitReq{ Tag: "PE Target Hit", Dirt: core.OdDirtShort, Force: true, }) } // 止损出场 if closePrice >= shortStopLoss { s.CloseOrders(&strat.ExitReq{ Tag: "PE Stop Loss Hit", Dirt: core.OdDirtShort, Force: true, }) } // 时间出场 if int64(holdBars) >= int64(maxHoldingBars) { s.CloseOrders(&strat.ExitReq{ Tag: "PE Timed Exit", Dirt: core.OdDirtShort, Force: true, }) } } }, } }