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" ) // StrategyState 保存每个品种的交易状态 type StrategyState struct { isLong bool // 当前是否持有多头仓位 isShort bool // 当前是否持有空头仓位 partialTpTriggered bool // 第一止盈是否已触发 stopPrice float64 // 止损价格 firstTpPrice float64 // 第一止盈价格 secondTpPrice float64 // 第二止盈价格 firstTpQty float64 // 第一止盈数量 positionSize float64 // 仓位大小 entryPrice float64 // 入场价格 } func J1665(pol *config.RunPolicyConfig) *strat.TradeStrat { // 定义可调超参数 fastLength := pol.DefInt("fastLength", 5, core.PNorm(3, 15)) emaRate := pol.Def("emaRate", 3.0, core.PNorm(1.5, 6.0)) rsiLength := pol.DefInt("rsiLength", 7, core.PNorm(3, 21)) atrPeriod := pol.DefInt("atrPeriod", 10, core.PNorm(5, 20)) stopLossRate := pol.Def("stopLossRate", 5.0, core.PNorm(2.0, 10.0)) firstTakeProfitRate := pol.Def("firstTakeProfitRate", 8.0, core.PNorm(4.0, 16.0)) secondTakeProfitRate := pol.Def("secondTakeProfitRate", 12.0, core.PNorm(6.0, 24.0)) firstTakeProfitQtyRate := pol.Def("firstTakeProfitQtyRate", 50.0, core.PNorm(25.0, 75.0)) leverageRate := pol.Def("leverageRate", 3.0, core.PNorm(1.0, 5.0)) // 计算慢速EMA长度 slowLength := int(float64(fastLength) * emaRate) return &strat.TradeStrat{ WarmupNum: slowLength + 26, // 确保有足够数据计算所有指标 HedgeOff: true, // 单向持仓 EachMaxLong: 1, // 最多一个多单 EachMaxShort: 1, // 最多一个空单 RunTimeFrames: []string{"8h"}, OnStartUp: func(s *strat.StratJob) { // 初始化策略状态 s.More = &StrategyState{ isLong: false, isShort: false, partialTpTriggered: false, stopPrice: math.NaN(), firstTpPrice: math.NaN(), secondTpPrice: math.NaN(), firstTpQty: math.NaN(), positionSize: math.NaN(), entryPrice: math.NaN(), } }, OnBar: func(s *strat.StratJob) { e := s.Env state, _ := s.More.(*StrategyState) // 计算技术指标 fastEMA := ta.EMA(e.Close, fastLength) slowEMA := ta.EMA(e.Close, slowLength) rsi := ta.RSI(e.Close, rsiLength) atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod) macd, signal := ta.MACD(e.Close, 12, 26, 9) // 获取当前值 closePrice := e.Close.Get(0) prevClosePrice := e.Close.Get(1) highPrice := e.High.Get(0) lowPrice := e.Low.Get(0) if math.IsNaN(closePrice) { return } // 检测MACD交叉 macdCross := macd.Cross(signal) macdGolden := macdCross == 1 // MACD金叉 macdDeath := macdCross == -1 // MACD死叉 // 生成交易信号 longCondition := fastEMA.Get(0) > slowEMA.Get(0) && macdGolden && rsi.Get(0) < 70 shortCondition := fastEMA.Get(0) < slowEMA.Get(0) && macdDeath && rsi.Get(0) > 30 // 平仓信号 exitLong := shortCondition || fastEMA.Get(0) < slowEMA.Get(0) exitShort := longCondition || fastEMA.Get(0) > slowEMA.Get(0) // 计算仓位大小 if !math.IsNaN(atr.Get(0)) && atr.Get(0) > 0 { maxRiskPerTrade := 0.05 // 修复错误1:使用动态账户权益而非固定值1000 stakeAmount := s.Strat.GetStakeAmount(s) basePosition := stakeAmount * maxRiskPerTrade atrAmount := atr.Get(0) * closePrice riskPosition := basePosition / atrAmount * leverageRate maxPosition := stakeAmount * 0.4 / closePrice state.positionSize = math.Min(riskPosition, maxPosition) // 修复错误6:将firstTpQty的初始化移到仓位计算函数内部 state.firstTpQty = state.positionSize * (firstTakeProfitQtyRate / 100.0) } // 多头入场 if longCondition && !state.isLong && !state.isShort && !math.IsNaN(state.positionSize) { state.entryPrice = closePrice state.stopPrice = closePrice * (1 - stopLossRate/100) state.firstTpPrice = closePrice * (1 + firstTakeProfitRate/100) state.secondTpPrice = closePrice * (1 + secondTakeProfitRate/100) s.OpenOrder(&strat.EnterReq{ Tag: "long", Amount: state.positionSize, StopLoss: state.stopPrice, StopLossTag: "stop_loss", }) state.isLong = true state.partialTpTriggered = false } // 空头入场 if shortCondition && !state.isShort && !state.isLong && !math.IsNaN(state.positionSize) { state.entryPrice = closePrice state.stopPrice = closePrice * (1 + stopLossRate/100) state.firstTpPrice = closePrice * (1 - firstTakeProfitRate/100) state.secondTpPrice = closePrice * (1 - secondTakeProfitRate/100) s.OpenOrder(&strat.EnterReq{ Tag: "short", Short: true, Amount: state.positionSize, StopLoss: state.stopPrice, StopLossTag: "stop_loss", }) state.isShort = true state.partialTpTriggered = false } // 修复错误2:在每个K线更新时重新计算止盈止损价格 if state.isLong { state.stopPrice = state.entryPrice * (1 - stopLossRate/100) state.firstTpPrice = state.entryPrice * (1 + firstTakeProfitRate/100) if !state.partialTpTriggered { state.secondTpPrice = state.entryPrice * (1 + secondTakeProfitRate/100) } // 修复错误3:在触发第一止盈时立即调整第二止盈价格 if !state.partialTpTriggered && (!math.IsNaN(prevClosePrice) && prevClosePrice >= state.firstTpPrice || highPrice >= state.firstTpPrice) { state.partialTpTriggered = true state.secondTpPrice = highPrice * (1 + 0.04) // 基于当前最高价再上涨4% } } if state.isShort { state.stopPrice = state.entryPrice * (1 + stopLossRate/100) state.firstTpPrice = state.entryPrice * (1 - firstTakeProfitRate/100) if !state.partialTpTriggered { state.secondTpPrice = state.entryPrice * (1 - secondTakeProfitRate/100) } // 修复错误3:在触发第一止盈时立即调整第二止盈价格 if !state.partialTpTriggered && (!math.IsNaN(prevClosePrice) && prevClosePrice <= state.firstTpPrice || lowPrice <= state.firstTpPrice) { state.partialTpTriggered = true state.secondTpPrice = lowPrice * (1 - 0.04) // 基于当前最低价再下跌4% } } // 多头平仓条件 if exitLong && state.isLong { s.CloseOrders(&strat.ExitReq{ Tag: "exit_long", Dirt: core.OdDirtLong, }) state.isLong = false state.partialTpTriggered = false } // 空头平仓条件 if exitShort && state.isShort { s.CloseOrders(&strat.ExitReq{ Tag: "exit_short", Dirt: core.OdDirtShort, }) state.isShort = false state.partialTpTriggered = false } }, OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq { state, _ := s.More.(*StrategyState) prevClosePrice := s.Env.Close.Get(1) highPrice := s.Env.High.Get(0) lowPrice := s.Env.Low.Get(0) if od.Short { // 空仓管理 if state.isShort { // 修复错误7:使用前一根K线的收盘价和当前K线的最高价/最低价检查止损 if (!math.IsNaN(prevClosePrice) && prevClosePrice >= state.stopPrice) || highPrice >= state.stopPrice { return &strat.ExitReq{ Tag: "short_stop_loss", OrderID: od.ID, } } // 修复错误7:使用前一根K线的收盘价和当前K线的最低价检查第一止盈 if !state.partialTpTriggered && ((!math.IsNaN(prevClosePrice) && prevClosePrice <= state.firstTpPrice) || lowPrice <= state.firstTpPrice) { return &strat.ExitReq{ Tag: "short_first_take_profit", OrderID: od.ID, Amount: state.firstTpQty, } } // 修复错误7:使用前一根K线的收盘价和当前K线的最低价检查第二止盈 if state.partialTpTriggered && ((!math.IsNaN(prevClosePrice) && prevClosePrice <= state.secondTpPrice) || lowPrice <= state.secondTpPrice) { state.isShort = false state.partialTpTriggered = false return &strat.ExitReq{ Tag: "short_second_take_profit", OrderID: od.ID, } } } } else { // 多仓管理 if state.isLong { // 修复错误7:使用前一根K线的收盘价和当前K线的最低价检查止损 if (!math.IsNaN(prevClosePrice) && prevClosePrice <= state.stopPrice) || lowPrice <= state.stopPrice { return &strat.ExitReq{ Tag: "long_stop_loss", OrderID: od.ID, } } // 修复错误7:使用前一根K线的收盘价和当前K线的最高价检查第一止盈 if !state.partialTpTriggered && ((!math.IsNaN(prevClosePrice) && prevClosePrice >= state.firstTpPrice) || highPrice >= state.firstTpPrice) { return &strat.ExitReq{ Tag: "long_first_take_profit", OrderID: od.ID, Amount: state.firstTpQty, } } // 修复错误7:使用前一根K线的收盘价和当前K线的最高价检查第二止盈 if state.partialTpTriggered && ((!math.IsNaN(prevClosePrice) && prevClosePrice >= state.secondTpPrice) || highPrice >= state.secondTpPrice) { state.isLong = false state.partialTpTriggered = false return &strat.ExitReq{ Tag: "long_second_take_profit", OrderID: od.ID, } } } } return nil }, OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) { state, _ := s.More.(*StrategyState) // 订单状态变化时更新策略状态 if chgType == strat.OdChgExitFill { if od.Short { // 空单完全平仓 state.isShort = false state.partialTpTriggered = false } else { // 多单完全平仓 state.isLong = false state.partialTpTriggered = false } } }, } }