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" ) // State4067 存储每个品种的策略状态 type State4067 struct { step int inTrade bool tradeDirection int // 1=多头, -1=空头, 0=无 entryPrice float64 tp1Hit bool buytp1Hit bool buySLPrice float64 buyTP1Price float64 buyTP2Price float64 selltp1Hit bool sellSLPrice float64 sellTP1Price float64 sellTP2Price float64 higherEMA float64 // 更高时间周期EMA值 } // MTFCross 多时间周期EMA交叉策略 func J4067(pol *config.RunPolicyConfig) *strat.TradeStrat { // 定义可调参数 fastPeriod := pol.DefInt("fastPeriod", 9, core.PNorm(5, 20)) slowRate := pol.Def("slowRate", 5.56, core.PNorm(2, 10)) higherEMARate := pol.Def("higherEMARate", 2, core.PNorm(1.5, 5)) slRate := pol.Def("slRate", 1.6, core.PNorm(0.5, 5)) tp1Rate := pol.Def("tp1Rate", 1.4, core.PNorm(0.5, 3)) beoffRate := pol.Def("beoffRate", 0.3, core.PNorm(0.1, 1)) atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 30)) // 交易时间控制参数 startHour := pol.DefInt("startHour", 1, core.PNorm(0, 23)) endHour := pol.DefInt("endHour", 25, core.PNorm(0, 47)) tradeOnMonday := pol.DefInt("tradeOnMonday", 1, core.PNorm(0, 1)) != 0 tradeOnTuesday := pol.DefInt("tradeOnTuesday", 1, core.PNorm(0, 1)) != 0 tradeOnWednesday := pol.DefInt("tradeOnWednesday", 1, core.PNorm(0, 1)) != 0 tradeOnThursday := pol.DefInt("tradeOnThursday", 1, core.PNorm(0, 1)) != 0 tradeOnFriday := pol.DefInt("tradeOnFriday", 1, core.PNorm(0, 1)) != 0 // 计算衍生参数 slowPeriod := int(float64(fastPeriod) * slowRate) higherPeriod := int(float64(slowPeriod) * higherEMARate) // 调整endHour为24小时格式 adjustedEndHour := endHour % 24 return &strat.TradeStrat{ WarmupNum: slowPeriod, // 预热K线数量 HedgeOff: true, // 单向持仓 EachMaxLong: 1, // 最多一个多头订单 EachMaxShort: 1, // 最多一个空头订单 RunTimeFrames: []string{"4h"}, // 初始化策略状态 OnStartUp: func(s *strat.StratJob) { s.More = &State4067{ step: 0, inTrade: false, tradeDirection: 0, entryPrice: math.NaN(), tp1Hit: false, buytp1Hit: false, selltp1Hit: false, buySLPrice: math.NaN(), buyTP1Price: math.NaN(), buyTP2Price: math.NaN(), sellSLPrice: math.NaN(), sellTP1Price: math.NaN(), sellTP2Price: math.NaN(), higherEMA: math.NaN(), } }, // 定义需要订阅的其他时间周期数据 OnPairInfos: func(s *strat.StratJob) []*strat.PairSub { return []*strat.PairSub{ {Pair: "_cur_", TimeFrame: "15m", WarmupNum: higherPeriod}, } }, // 处理更高时间周期数据 OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) { state, _ := s.More.(*State4067) // 计算更高时间周期的EMA higherEMA := ta.EMA(e.Close, higherPeriod) if higherEMA.Len() > 0 { state.higherEMA = higherEMA.Get(0) } }, // 主策略逻辑 OnBar: func(s *strat.StratJob) { e := s.Env state, _ := s.More.(*State4067) // 计算技术指标 fastEMA := ta.EMA(e.Close, fastPeriod) slowEMA := ta.EMA(e.Close, slowPeriod) atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod) // 获取当前值 closePrice := e.Close.Get(0) fastEMAV := fastEMA.Get(0) slowEMAV := slowEMA.Get(0) atrV := atr.Get(0) // 检查数据有效性 if math.IsNaN(closePrice) || math.IsNaN(fastEMAV) || math.IsNaN(slowEMAV) || math.IsNaN(atrV) || math.IsNaN(state.higherEMA) { return } // 计算止损止盈距离 slDist := atrV * slRate tp1Dist := atrV * tp1Rate tp2Dist := atrV * tp1Rate * 2 // 第二止盈是第一止盈的2倍 beoffDist := atrV * beoffRate // 检查交易时间 t := time.UnixMilli(e.TimeStart) isTradingTime := checkTradingTime(t, startHour, adjustedEndHour, tradeOnMonday, tradeOnTuesday, tradeOnWednesday, tradeOnThursday, tradeOnFriday) // 更新信号状态步骤 updateStep(state, fastEMA, slowEMA) // 生成交易信号(仅在交易时间内) if isTradingTime { if state.step == 1 && !state.inTrade && fastEMAV > slowEMAV && fastEMAV > state.higherEMA { // 买入信号 state.inTrade = true state.tradeDirection = 1 state.entryPrice = closePrice state.buytp1Hit = false state.tp1Hit = false state.buySLPrice = state.entryPrice - slDist state.buyTP1Price = state.entryPrice + tp1Dist state.buyTP2Price = state.entryPrice + tp2Dist state.step = 3 // 开多单 //s.OpenOrder(&strat.EnterReq{ // Tag: "Buy", // Short: false, // StopLoss: state.buySLPrice, // TakeProfit: state.buyTP1Price, // StopLossVal: slDist, //}) } else if state.step == 2 && !state.inTrade && fastEMAV < slowEMAV && fastEMAV < state.higherEMA { // 卖出信号 state.inTrade = true state.tradeDirection = -1 state.entryPrice = closePrice state.selltp1Hit = false state.tp1Hit = false state.sellSLPrice = state.entryPrice + slDist state.sellTP1Price = state.entryPrice - tp1Dist state.sellTP2Price = state.entryPrice - tp2Dist state.step = 4 // 开空单 s.OpenOrder(&strat.EnterReq{ Tag: "Sell", Short: true, StopLoss: state.sellSLPrice, TakeProfit: state.sellTP1Price, StopLossVal: slDist, }) } } // 管理多头仓位 if state.inTrade && state.tradeDirection == 1 && len(s.LongOrders) > 0 { od := s.LongOrders[0] highPrice := e.High.Get(0) lowPrice := e.Low.Get(0) // 检查是否达到第一止盈目标 if highPrice >= state.buyTP1Price && !state.tp1Hit { state.tp1Hit = true state.buytp1Hit = true // 移动止损到保本位置 state.buySLPrice = state.entryPrice + beoffDist // 平仓25% s.CloseOrders(&strat.ExitReq{ Tag: "TP1 Hit", Dirt: core.OdDirtLong, ExitRate: 0.25, OrderType: core.OrderTypeMarket, }) // 更新剩余仓位的止损止盈 od.SetStopLoss(&ormo.ExitTrigger{ Price: state.buySLPrice, Rate: 1.0, Tag: "Breakeven", }) od.SetTakeProfit(&ormo.ExitTrigger{ Price: state.buyTP2Price, Rate: 1.0, Tag: "TP2", }) } // 检查是否触发止损 if lowPrice <= state.buySLPrice && !state.tp1Hit { // 止损出场 s.CloseOrders(&strat.ExitReq{ Tag: "SL Hit", Dirt: core.OdDirtLong, ExitRate: 1.0, OrderType: core.OrderTypeMarket, }) resetState(state) } // 检查是否达到第二止盈目标 if state.tp1Hit && highPrice >= state.buyTP2Price { // 第二止盈出场 s.CloseOrders(&strat.ExitReq{ Tag: "TP2 Hit", Dirt: core.OdDirtLong, ExitRate: 1.0, OrderType: core.OrderTypeMarket, }) resetState(state) } // 检查保本止损是否触发 if state.tp1Hit && lowPrice <= state.buySLPrice { // 保本止损出场 s.CloseOrders(&strat.ExitReq{ Tag: "Breakeven Hit", Dirt: core.OdDirtLong, ExitRate: 1.0, OrderType: core.OrderTypeMarket, }) resetState(state) } } // 管理空头仓位 if state.inTrade && state.tradeDirection == -1 && len(s.ShortOrders) > 0 { od := s.ShortOrders[0] highPrice := e.High.Get(0) lowPrice := e.Low.Get(0) // 检查是否达到第一止盈目标 if lowPrice <= state.sellTP1Price && !state.tp1Hit { state.tp1Hit = true state.selltp1Hit = true // 移动止损到保本位置 state.sellSLPrice = state.entryPrice - beoffDist // 平仓25% s.CloseOrders(&strat.ExitReq{ Tag: "TP1 Hit", Dirt: core.OdDirtShort, ExitRate: 0.25, OrderType: core.OrderTypeMarket, }) // 更新剩余仓位的止损止盈 od.SetStopLoss(&ormo.ExitTrigger{ Price: state.sellSLPrice, Rate: 1.0, Tag: "Breakeven", }) od.SetTakeProfit(&ormo.ExitTrigger{ Price: state.sellTP2Price, Rate: 1.0, Tag: "TP2", }) } // 检查是否触发止损 if highPrice >= state.sellSLPrice && !state.tp1Hit { // 止损出场 s.CloseOrders(&strat.ExitReq{ Tag: "SL Hit", Dirt: core.OdDirtShort, ExitRate: 1.0, OrderType: core.OrderTypeMarket, }) resetState(state) } // 检查是否达到第二止盈目标 if state.tp1Hit && lowPrice <= state.sellTP2Price { // 第二止盈出场 s.CloseOrders(&strat.ExitReq{ Tag: "TP2 Hit", Dirt: core.OdDirtShort, ExitRate: 1.0, OrderType: core.OrderTypeMarket, }) resetState(state) } // 检查保本止损是否触发 if state.tp1Hit && highPrice >= state.sellSLPrice { // 保本止损出场 s.CloseOrders(&strat.ExitReq{ Tag: "Breakeven Hit", Dirt: core.OdDirtShort, ExitRate: 1.0, OrderType: core.OrderTypeMarket, }) resetState(state) } } // 更新交易状态 if state.inTrade && state.tradeDirection == 1 && state.step == 2 { // 多头收到卖出信号 state.inTrade = false state.tradeDirection = 0 state.step = 2 } if state.inTrade && state.tradeDirection == -1 && state.step == 1 { // 空头收到买入信号 state.inTrade = false state.tradeDirection = 0 state.step = 1 } }, // 自定义订单退出逻辑 OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq { state, _ := s.More.(*State4067) // 多头订单检查 if !od.Short && state.inTrade && state.tradeDirection == 1 { lowPrice := s.Env.Low.Get(0) // 检查是否触发止损 if lowPrice <= state.buySLPrice { return &strat.ExitReq{ Tag: "SL Hit", Dirt: core.OdDirtLong, ExitRate: 1.0, } } // 检查是否达到第二止盈目标 if state.tp1Hit { highPrice := s.Env.High.Get(0) if highPrice >= state.buyTP2Price { return &strat.ExitReq{ Tag: "TP2 Hit", Dirt: core.OdDirtLong, ExitRate: 1.0, } } } } // 空头订单检查 if od.Short && state.inTrade && state.tradeDirection == -1 { highPrice := s.Env.High.Get(0) // 检查是否触发止损 if highPrice >= state.sellSLPrice { return &strat.ExitReq{ Tag: "SL Hit", Dirt: core.OdDirtShort, ExitRate: 1.0, } } // 检查是否达到第二止盈目标 if state.tp1Hit { lowPrice := s.Env.Low.Get(0) if lowPrice <= state.sellTP2Price { return &strat.ExitReq{ Tag: "TP2 Hit", Dirt: core.OdDirtShort, ExitRate: 1.0, } } } } return nil }, } } // checkTradingTime 检查当前时间是否在交易时间内 func checkTradingTime(t time.Time, startHour, adjustedEndHour int, tradeOnMonday, tradeOnTuesday, tradeOnWednesday, tradeOnThursday, tradeOnFriday bool) bool { // 获取当前小时和星期几 currentHour := t.Hour() currentDay := t.Weekday() // 检查是否在交易小时内 isTradingHour := false if startHour < adjustedEndHour { isTradingHour = currentHour >= startHour && currentHour < adjustedEndHour } else { isTradingHour = currentHour >= startHour || currentHour < adjustedEndHour } // 检查是否在交易日内 isTradingDay := false switch currentDay { case time.Monday: isTradingDay = tradeOnMonday case time.Tuesday: isTradingDay = tradeOnTuesday case time.Wednesday: isTradingDay = tradeOnWednesday case time.Thursday: isTradingDay = tradeOnThursday case time.Friday: isTradingDay = tradeOnFriday } return isTradingHour && isTradingDay } // updateStep 更新信号状态步骤 func updateStep(state *State4067, fastEMA, slowEMA *ta.Series) { // 检查EMA交叉 crossUp := fastEMA.Cross(slowEMA) crossDown := slowEMA.Cross(fastEMA) // 更新状态步骤 if (state.step == 0 || state.step == 4) && crossUp == 1 && fastEMA.Get(0) > state.higherEMA { state.step = 1 } if state.step == 3 && crossUp == 1 && fastEMA.Get(0) > state.higherEMA { state.step = 3 } if state.step == 2 && crossUp == 1 && fastEMA.Get(0) > state.higherEMA { state.step = 1 } if (state.step == 0 || state.step == 3) && crossDown == 1 && fastEMA.Get(0) < state.higherEMA { state.step = 2 } if state.step == 4 && crossDown == 1 && fastEMA.Get(0) < state.higherEMA { state.step = 4 } if state.step == 1 && crossDown == 1 && fastEMA.Get(0) < state.higherEMA { state.step = 2 } } // resetState 重置策略状态 func resetState(state *State4067) { state.inTrade = false state.tradeDirection = 0 state.entryPrice = math.NaN() state.tp1Hit = false state.buytp1Hit = false state.selltp1Hit = false state.buySLPrice = math.NaN() state.buyTP1Price = math.NaN() state.buyTP2Price = math.NaN() state.sellSLPrice = math.NaN() state.sellTP1Price = math.NaN() state.sellTP2Price = math.NaN() state.step = 0 }