package llmjob import ( "fmt" "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/strat" ta "github.com/banbox/banta" "gonum.org/v1/gonum/floats" "math" "sort" ) /* 基于初始的3087进行了简化,只保留盈利较好的14-26 */ // DCAStrategyState 存储每个品种的策略状态 type DCAStrategyState struct { currentSO int // 当前安全订单计数器 firstPrice float64 // 初始订单价格 totalQtys float64 // 总交易数量 totalCost float64 // 累计花费 realTotQtys float64 // 总交易数量 realTotCost float64 // 累计花费 } // calculateSafetyOrderThreshold 计算安全订单触发价格阈值 func calculateSafetyOrderThreshold(initPrice, priceDeviation, stepScale float64, currentSO int) float64 { curSO := float64(currentSO) if stepScale == 1.0 { return initPrice - initPrice*priceDeviation*stepScale*curSO } rate := priceDeviation*math.Pow(stepScale, curSO) - priceDeviation return initPrice - initPrice*(rate/(stepScale-1)) } // DynamicDCA 动态DCA策略 func J3087(pol *config.RunPolicyConfig) *strat.TradeStrat { // 定义可调参数 priceDeviation := pol.Def("priceDeviation", 0.82, core.PNorm(0.5, 3.0)) volumeScale := pol.Def("volumeScale", 1.16, core.PNorm(1.05, 1.5)) stepScale := pol.Def("stepScale", 1.06, core.PNorm(1.05, 1.3)) takeProfit := pol.Def("takeProfit", 1.15, core.PNorm(0.5, 2.5)) baseOrder := pol.Def("baseOrder", 10.0, core.PNorm(5, 20)) startLvl := pol.DefInt("startLvl", 14.0, core.PNorm(1, 20)) endLvl := pol.DefInt("endLvl", 26.0, core.PNorm(3, 30)) maxSafeOrders := pol.DefInt("maxSafeOrders", 30, core.PNorm(5, 50)) lenAtr := 20 baseAtrLen := int(math.Round(float64(lenAtr) * 4.3)) var rates []float64 // 14/15/16/17/18/19/20/21/22/23/24/25/26 return &strat.TradeStrat{ WarmupNum: 1, HedgeOff: true, // 单向持仓 RunTimeFrames: []string{"15m"}, OnStartUp: func(s *strat.StratJob) { // 初始化策略状态 s.More = &DCAStrategyState{} }, OnBar: func(s *strat.StratJob) { e := s.Env state, _ := s.More.(*DCAStrategyState) // 计算OHLC4加权均价 price := (e.High.Get(0) + e.Low.Get(0) + e.Close.Get(0) + e.Open.Get(0)) / 4 closeP := e.Close.Get(0) atr := ta.ATR(e.High, e.Low, e.Close, lenAtr) atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0) openRate := 0.004 * closeP / atrBase if openRate < 1 { openRate = math.Sqrt(openRate) } openRate = max(0.7, min(1.5, openRate)) // 计算止盈水平 hasOrders := state.currentSO > 0 var avgPrice, realAvgPrice float64 if hasOrders && state.totalQtys > 0 { avgPrice = state.totalCost / state.totalQtys realAvgPrice = state.realTotCost / state.realTotQtys } takeProfitLevel := avgPrice * (1 + takeProfit/100) // 初始建仓逻辑 if state.currentSO == 0 && price > 0 { // 计算买入数量 qty := baseOrder / price // 首次买入表现不佳,禁止实际买入 state.totalQtys += qty state.totalCost += qty * closeP state.firstPrice = price state.currentSO = 1 } // 计算安全订单触发价格阈值 priceRate := priceDeviation / 100 threshold := calculateSafetyOrderThreshold(state.firstPrice, priceRate, stepScale, state.currentSO) // 动态补仓逻辑 if hasOrders && price <= threshold && state.currentSO <= maxSafeOrders { // 计算补仓数量 orderQty := baseOrder * openRate * math.Pow(volumeScale, float64(state.currentSO-1)) / price // endLvl用于实际下单。maxSafeOrders用于止盈价格 if state.currentSO >= startLvl && state.currentSO <= endLvl { s.OpenOrder(&strat.EnterReq{ Tag: fmt.Sprintf("%v", state.currentSO), Amount: orderQty, Log: true, }) state.realTotQtys += orderQty state.realTotCost += orderQty * closeP } state.totalQtys += orderQty state.totalCost += orderQty * closeP state.currentSO += 1 } // 止盈平仓逻辑 if price >= takeProfitLevel && hasOrders { s.CloseOrders(&strat.ExitReq{ Tag: "TakeProfit", }) if realAvgPrice > 0 { rates = append(rates, takeProfitLevel/realAvgPrice) } // 重置状态变量 state.currentSO = 0 state.totalQtys = 0.0 state.totalCost = 0 state.realTotQtys = 0 state.realTotCost = 0 state.firstPrice = 0 } }, OnShutDown: func(s *strat.StratJob) { if len(rates) == 0 { return } sort.Float64s(rates) step := len(rates) / 7 for i := 0; i < len(rates); i += step { block := rates[i:min(len(rates), i+step)] avg := floats.Sum(block) / float64(len(block)) fmt.Printf("rate: %v: %v\n", i, avg) } rates = nil }, } }