package llmjob import ( "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/strat" ta "github.com/banbox/banta" "math" ) type TrendlineData struct { upper float64 lower float64 slopePh float64 slopePl float64 upos int dnos int prevUpos int prevDnos int } func J3464(pol *config.RunPolicyConfig) *strat.TradeStrat { length := pol.DefInt("length", 13, core.PNorm(5, 30)) mult := pol.Def("mult", 1.05, core.PNorm(0.5, 2.0)) calcMethod := pol.Def("calcMethod", 0, core.PUniform(0, 2)) // 0: Atr, 1: Stdev, 2: Linreg _ = pol.Def("backpaint", 1.0, core.PUniform(0, 1)) == 1.0 // 使用下划线忽略未使用的变量 return &strat.TradeStrat{ WarmupNum: length * 2, HedgeOff: true, // 单向持仓 RunTimeFrames: []string{"4h"}, OnStartUp: func(s *strat.StratJob) { s.More = &TrendlineData{ upper: 0, lower: 0, slopePh: 0, slopePl: 0, upos: 0, dnos: 0, prevUpos: 0, prevDnos: 0, } }, OnBar: func(s *strat.StratJob) { e := s.Env data, _ := s.More.(*TrendlineData) // 获取当前价格 closePrice := e.Close.Get(0) if math.IsNaN(closePrice) { return } // 检测支点 - 实现与PineScript的pivothigh和pivotlow相似的功能 // 需要检查当前K线是否是过去length周期内的最高点,并且左右两侧都有length个K线 ph := false pl := false // 确保有足够的历史数据 if e.Close.Len() > length*2 { // 检查当前K线是否是支点高 currentHigh := e.High.Get(length) isPivotHigh := true for i := 0; i < length; i++ { if e.High.Get(i) >= currentHigh || e.High.Get(length*2-i) >= currentHigh { isPivotHigh = false break } } // 检查当前K线是否是支点低 currentLow := e.Low.Get(length) isPivotLow := true for i := 0; i < length; i++ { if e.Low.Get(i) <= currentLow || e.Low.Get(length*2-i) <= currentLow { isPivotLow = false break } } ph = isPivotHigh && !math.IsNaN(currentHigh) pl = isPivotLow && !math.IsNaN(currentLow) } // 计算斜率 var slope float64 method := int(calcMethod) switch method { case 0: // Atr atr := ta.ATR(e.High, e.Low, e.Close, length) slope = atr.Get(0) / float64(length) * mult case 1: // Stdev stdDev := ta.StdDev(e.Close, length) slope = stdDev.Get(0) / float64(length) * mult case 2: // Linreg // 简化版的线性回归斜率计算 n := float64(length) sumX := n * (n - 1) / 2 sumY := ta.SMA(e.Close, length).Get(0) * n sumXY := 0.0 sumX2 := 0.0 for i := 0; i < length; i++ { x := float64(i) y := e.Close.Get(i) if !math.IsNaN(y) { sumXY += x * y sumX2 += x * x } } // 计算斜率 denominator := n*sumX2 - sumX*sumX if denominator != 0 { slope = (n*sumXY - sumX*sumY) / denominator * mult } else { slope = 0 } } // 更新趋势线斜率和值 if ph { data.slopePh = slope data.upper = e.High.Get(length) } else { data.upper = data.upper - data.slopePh } if pl { data.slopePl = slope data.lower = e.Low.Get(length) } else { data.lower = data.lower + data.slopePl } // 保存前一个状态 data.prevUpos = data.upos data.prevDnos = data.dnos // 检测突破状态 if ph { data.upos = 0 } else if closePrice > data.upper-data.slopePh*float64(length) { data.upos = 1 } if pl { data.dnos = 0 } else if closePrice < data.lower+data.slopePl*float64(length) { data.dnos = 1 } // 检测突破并生成交易信号 upBreakout := data.upos > data.prevUpos dnBreakout := data.dnos > data.prevDnos if upBreakout && len(s.LongOrders) == 0 { s.OpenOrder(&strat.EnterReq{ Tag: "Up Breakout", }) } if dnBreakout && len(s.ShortOrders) == 0 { s.OpenOrder(&strat.EnterReq{ Tag: "Down Breakout", Short: true, }) } }, } }