// 修复后的banbot完整策略代码 package llmjob import ( "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/orm/ormo" "github.com/banbox/banbot/strat" ta "github.com/banbox/banta" "math" "time" ) type MfiStrategyState struct { DayStart int64 DayLow float64 DayHigh float64 EntryTime int64 LongSlPrice float64 ShortSlPrice float64 LongTpPrice float64 ShortTpPrice float64 TrailStop float64 TrailStopShort float64 EntryHigh float64 EntryLow float64 } func J5129(pol *config.RunPolicyConfig) *strat.TradeStrat { mfiPeriod := pol.DefInt("mfiPeriod", 3, core.PNorm(2, 10)) barBodySize := pol.Def("barBodySize", 0.65, core.PNorm(0.3, 0.8)) maPeriod := pol.DefInt("maPeriod", 120, core.PNorm(20, 200)) useMaFilter := pol.Def("useMaFilter", 1.0, nil) useShorts := pol.Def("useShorts", 1.0, nil) slType := pol.Def("slType", 0, nil) // 0:Strategy Stop, 1:Swing, 2:ATR swingLookback := pol.DefInt("swingLookback", 10, core.PNorm(5, 30)) swingPercInc := pol.Def("swingPercInc", 0.03, core.PNorm(0.01, 0.1)) atrLength := pol.DefInt("atrLength", 14, core.PNorm(7, 30)) atrMultiple := pol.Def("atrMultiple", 6.5, core.PNorm(2.0, 10.0)) rewardRatio := pol.Def("rewardRatio", 3.2, core.PNorm(1.5, 4.0)) useTpSl := pol.Def("useTpSl", 1.0, nil) useTimedExit := pol.Def("useTimedExit", 0.0, nil) useTrailingStop := pol.Def("useTrailingStop", 0.0, nil) allowDirectPositionReverse := pol.Def("allowDirectPositionReverse", 1.0, nil) return &strat.TradeStrat{ Name: "Money Flow Index 5 min Strategy", Version: 1, WarmupNum: 100, HedgeOff: false, EachMaxLong: 1, EachMaxShort: 1, RunTimeFrames: []string{"15m"}, OnStartUp: func(s *strat.StratJob) { s.More = &MfiStrategyState{ DayStart: -1, DayLow: math.Inf(1), DayHigh: math.Inf(-1), EntryTime: 0, LongSlPrice: math.NaN(), ShortSlPrice: math.NaN(), LongTpPrice: math.NaN(), ShortTpPrice: math.NaN(), TrailStop: math.NaN(), TrailStopShort: math.NaN(), EntryHigh: math.NaN(), EntryLow: math.NaN(), } }, OnBar: func(s *strat.StratJob) { e := s.Env now := e.TimeStop c, h, l, o := e.Close.Get(0), e.High.Get(0), e.Low.Get(0), e.Open.Get(0) if math.IsNaN(c) || math.IsNaN(h) || math.IsNaN(l) { return } // 更新交易日状态 t := time.UnixMilli(now) dayStartMs := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()).UnixMilli() m := s.More.(*MfiStrategyState) if m.DayStart != dayStartMs { m.DayStart = dayStartMs m.DayLow = l m.DayHigh = h } else { if l < m.DayLow { m.DayLow = l } if h > m.DayHigh { m.DayHigh = h } } // 计算指标 mfi := ta.MFI(e, mfiPeriod) smaVal := ta.SMA(e.Close, maPeriod) maFilter := c > smaVal.Get(0) atr := ta.ATR(e.High, e.Low, e.Close, atrLength).Get(0) // K线实体判断 - 修复错误3:与原始策略保持一致 barSize := h - l var barBodySizeVal float64 if c > o { barBodySizeVal = (o - c) * -1 } else { barBodySizeVal = (o - c) } shortWicks := barBodySizeVal > barSize*barBodySize // 交易信号 prevMfi := mfi.Get(1) buySignal := !math.IsNaN(prevMfi) && prevMfi == 100 && c > o && shortWicks && (useMaFilter == 0.0 || maFilter) sellSignal := false if useShorts == 1.0 { sellSignal = !math.IsNaN(prevMfi) && prevMfi == 0 && c < o && shortWicks && (useMaFilter == 0.0 || !maFilter) } // 检查60分钟退出规则 - 修复错误2:添加60分钟退出规则 if useTimedExit == 1.0 && m.EntryTime > 0 { timeSinceEntry := (now - m.EntryTime) / 60000 // 转换为分钟 if timeSinceEntry >= 60 { // 60分钟后平仓 if len(s.LongOrders) > 0 || len(s.ShortOrders) > 0 { s.CloseOrders(&strat.ExitReq{Dirt: core.OdDirtBoth}) m.EntryTime = 0 } } } // 计算止损止盈 - 修复错误1和4:记录开仓时的止损价格 var longSl, shortSl, longTp, shortTp float64 if useTpSl == 1.0 { switch int(slType) { case 0: // Strategy Stop longSl = m.DayLow * (1 - swingPercInc) shortSl = m.DayHigh * (1 + swingPercInc) case 1: // Swing Lo/Hi ll := ta.Lowest(e.Low, swingLookback).Get(0) * (1 - swingPercInc) hh := ta.Highest(e.High, swingLookback).Get(0) * (1 + swingPercInc) longSl = ll shortSl = hh case 2: // ATR Stop ohlc4 := (o + h + l + c) / 4 longSl = ohlc4 - atr*atrMultiple shortSl = ohlc4 + atr*atrMultiple } longTp = c + (c-longSl)*rewardRatio shortTp = c - (shortSl-c)*rewardRatio } // 更新追踪止损 - 修复错误5:添加追踪止损功能 if useTrailingStop == 1.0 { if len(s.LongOrders) > 0 { for _, pos := range s.LongOrders { if pos.Status == ormo.InOutStatusFullEnter { // 多头仓位 if math.IsNaN(m.EntryHigh) || h > m.EntryHigh { m.EntryHigh = h } trailOffset := pos.InitPrice - m.LongSlPrice newTrailStop := m.EntryHigh - trailOffset if math.IsNaN(m.TrailStop) || newTrailStop > m.TrailStop { m.TrailStop = newTrailStop } } } } if len(s.ShortOrders) > 0 { for _, pos := range s.ShortOrders { if pos.Status == ormo.InOutStatusFullEnter { // 空头仓位 if math.IsNaN(m.EntryLow) || l < m.EntryLow { m.EntryLow = l } trailOffset := m.ShortSlPrice - pos.InitPrice newTrailStop := m.EntryLow + trailOffset if math.IsNaN(m.TrailStopShort) || newTrailStop < m.TrailStopShort { m.TrailStopShort = newTrailStop } } } } } // 开仓 - 修复错误6:检查持仓数量和挂单数量 canOpenLong := len(s.LongOrders) == 0 && (len(s.ShortOrders) == 0 || (allowDirectPositionReverse == 1.0 && len(s.ShortOrders) > 0)) canOpenShort := len(s.ShortOrders) == 0 && (len(s.LongOrders) == 0 || (allowDirectPositionReverse == 1.0 && len(s.LongOrders) > 0)) if buySignal && canOpenLong { // 记录开仓时的止损价格 m.LongSlPrice = longSl m.LongTpPrice = longTp m.EntryHigh = h m.EntryTime = now req := &strat.EnterReq{ Tag: "long", StopLoss: longSl, TakeProfit: longTp, StopLossTag: "sl", TakeProfitTag: "tp", } s.OpenOrder(req) } if sellSignal && canOpenShort { // 记录开仓时的止损价格 m.ShortSlPrice = shortSl m.ShortTpPrice = shortTp m.EntryLow = l m.EntryTime = now req := &strat.EnterReq{ Tag: "short", Short: true, StopLoss: shortSl, TakeProfit: shortTp, StopLossTag: "sl", TakeProfitTag: "tp", } s.OpenOrder(req) } // 更新现有订单的止损止盈 if len(s.LongOrders) > 0 && !math.IsNaN(m.LongSlPrice) { for _, order := range s.LongOrders { if order.Status == ormo.InOutStatusInit { order.SetStopLoss(&ormo.ExitTrigger{Price: m.LongSlPrice, Tag: "sl"}) order.SetTakeProfit(&ormo.ExitTrigger{Price: m.LongTpPrice, Tag: "tp"}) if useTrailingStop == 1.0 && !math.IsNaN(m.TrailStop) { order.SetStopLoss(&ormo.ExitTrigger{Price: m.TrailStop, Tag: "sl"}) } } } } if len(s.ShortOrders) > 0 && !math.IsNaN(m.ShortSlPrice) { for _, order := range s.ShortOrders { if order.Status == ormo.InOutStatusInit { order.SetStopLoss(&ormo.ExitTrigger{Price: m.ShortSlPrice, Tag: "sl"}) order.SetTakeProfit(&ormo.ExitTrigger{Price: m.ShortTpPrice, Tag: "tp"}) if useTrailingStop == 1.0 && !math.IsNaN(m.TrailStopShort) { order.SetStopLoss(&ormo.ExitTrigger{Price: m.TrailStopShort, Tag: "sl"}) } } } } }, } }