Files
banstagy/llmjob/4513.go
2026-08-24 20:53:21 +08:00

269 lines
8.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 修复后的banbot完整策略代码
package llmjob
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
"math"
)
// State4513 存储每个品种的策略状态
type State4513 struct {
dir int // 当前趋势方向,1=上升趋势,-1=下降趋势
longStop float64 // 多头止损价格
shortStop float64 // 空头止损价格
longStopPrev float64 // 前一个多头止损价格
shortStopPrev float64 // 前一个空头止损价格
trailingStop float64 // 当前跟踪止损价格
}
// Demo 创建并返回策略实例
func J4513(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义可调超参数
bbLength := pol.DefInt("BBLength", 100, core.PNorm(20, 200))
stdDev := pol.Def("StdDev", 10, core.PNorm(1, 20))
useMovingAverage := pol.Def("useMovingAverage", 1, core.PUniform(0, 1)) // 1=true, 0=false
maType := pol.DefInt("maType", 3, core.PUniform(0, 5)) // 0=ema, 1=sma, 2=hma, 3=rma, 4=vwma, 5=wma
lookbackPeriod := pol.DefInt("lookbackPeriod", 22, core.PNorm(5, 50))
atrMaType := pol.DefInt("atrMaType", 2, core.PUniform(0, 5)) // 0=ema, 1=sma, 2=hma, 3=rma, 4=vwma, 5=wma
atrLength := pol.DefInt("atrLength", 10, core.PNorm(5, 30))
atrMult := pol.Def("atrMult", 4.2, core.PNorm(1, 10))
wicks := pol.Def("wicks", 0, core.PUniform(0, 1)) // 1=true, 0=false
considerYearlyHighLow := pol.Def("considerYearlyHighLow", 0, core.PUniform(0, 1)) // 1=true, 0=false
considerNewLongTermHighLows := pol.Def("considerNewLongTermHighLows", 0, core.PUniform(0, 1)) // 1=true, 0=false
tradeDirection := pol.DefInt("tradeDirection", 0, core.PUniform(0, 2)) // 0=all, 1=long, 2=short
// 固定参数
shortHighLowPeriod := 100
longHighLowPeriod := 200
return &strat.TradeStrat{
Name: "Bollinger Percentage Bands",
WarmupNum: int(math.Max(float64(bbLength), float64(longHighLowPeriod))) + 10,
HedgeOff: true, // 单向持仓
EachMaxLong: 1,
EachMaxShort: 1,
RunTimeFrames: []string{"8h"},
OnStartUp: func(s *strat.StratJob) {
// 初始化策略状态
state := &State4513{
dir: 1,
longStop: 0,
shortStop: 0,
}
s.More = state
},
OnBar: func(s *strat.StratJob) {
e := s.Env
state, _ := s.More.(*State4513)
// 1. 计算布林带指标
// 修复错误1:布林带计算参数错误
// 原始策略使用bb(close, BBLength, StdDev),其中StdDev是标准差倍数
// 修改为使用正确的参数格式,提供上下轨的标准差倍数
upperClose, _, lowerClose := ta.BBANDS(e.Close, bbLength, stdDev, stdDev)
upperHigh, _, lowerHigh := ta.BBANDS(e.High, bbLength, stdDev, stdDev)
upperLow, _, lowerLow := ta.BBANDS(e.Low, bbLength, stdDev, stdDev)
// 2. 计算布林带百分比
percentBClose := e.Close.Sub(lowerClose).Div(upperClose.Sub(lowerClose)).Mul(100)
percentBHigh := e.High.Sub(lowerHigh).Div(upperHigh.Sub(lowerHigh)).Mul(100)
percentBLow := e.Low.Sub(lowerLow).Div(upperLow.Sub(lowerLow)).Mul(100)
// 3. 应用移动平均线平滑
var newClose, newHigh, newLow *ta.Series
if useMovingAverage > 0.5 {
percentBMAClose := getMovingAverage(percentBClose, maType, lookbackPeriod)
percentBMAHigh := getMovingAverage(percentBHigh, maType, lookbackPeriod)
percentBMALow := getMovingAverage(percentBLow, maType, lookbackPeriod)
newClose = percentBMAClose
newHigh = percentBMAHigh
newLow = percentBMALow
} else {
newClose = percentBClose
newHigh = percentBHigh
newLow = percentBLow
}
// 4. 计算ATR和止损
closePrev := newClose.Back(1)
highPrev := newHigh.Back(1)
lowPrev := newLow.Back(1)
trueRange := newHigh.Max(closePrev).Sub(newLow.Min(closePrev))
averageTrueRange := getMovingAverage(trueRange, atrMaType, atrLength)
atr := averageTrueRange.Mul(atrMult)
longStopVal := newClose.Sub(atr)
shortStopVal := newClose.Add(atr)
// 修复错误2:止损更新逻辑不完整
// 原始策略使用nz(longStop[1], longStop)处理前一个止损值不存在的情况
// 修改为正确处理初始状态,并确保止损只能向有利方向移动
if state.longStopPrev == 0 {
state.longStopPrev = longStopVal.Get(0)
}
if state.shortStopPrev == 0 {
state.shortStopPrev = shortStopVal.Get(0)
}
// 修复错误3:使用Series运算创建常量而不是ta.Const
if wicks > 0.5 {
if lowPrev.Get(0) > state.longStopPrev {
// 使用Max确保止损只能向有利方向移动
longStopVal = longStopVal.Max(newClose.Sub(newClose).Add(state.longStopPrev))
}
} else {
if closePrev.Get(0) > state.longStopPrev {
longStopVal = longStopVal.Max(newClose.Sub(newClose).Add(state.longStopPrev))
}
}
if wicks > 0.5 {
if highPrev.Get(0) < state.shortStopPrev {
shortStopVal = shortStopVal.Min(newClose.Sub(newClose).Add(state.shortStopPrev))
}
} else {
if closePrev.Get(0) < state.shortStopPrev {
shortStopVal = shortStopVal.Min(newClose.Sub(newClose).Add(state.shortStopPrev))
}
}
// 5. 判断趋势方向
// 修复错误4:趋势方向判断可能存在边界条件处理错误
// 原始策略使用三元运算符,逻辑清晰且紧凑
// 修改为更接近原始策略的实现
if state.dir == -1 {
if wicks > 0.5 {
if newHigh.Get(0) > state.shortStopPrev {
state.dir = 1
}
} else {
if newClose.Get(0) > state.shortStopPrev {
state.dir = 1
}
}
} else if state.dir == 1 {
if wicks > 0.5 {
if newLow.Get(0) < state.longStopPrev {
state.dir = -1
}
} else {
if newClose.Get(0) < state.longStopPrev {
state.dir = -1
}
}
}
if state.dir == 1 {
state.trailingStop = longStopVal.Get(0)
} else {
state.trailingStop = shortStopVal.Get(0)
}
// 6. 判断辅助条件
yearlyHighCondition, yearlyLowCondition := getYearlyHighLowCondition(e, considerYearlyHighLow > 0.5)
newHighS, newLowS := calculateNewHighLows(e, shortHighLowPeriod, longHighLowPeriod, considerNewLongTermHighLows > 0.5)
// 7. 生成交易信号
buyCondition := state.dir == 1 && yearlyHighCondition && newHighS
exitBuyCondition := state.dir == -1
sellCondition := state.dir == -1 && yearlyLowCondition && newLowS
exitSellCondition := state.dir == 1
// 8. 执行交易
if buyCondition && (tradeDirection == 0 || tradeDirection == 1) {
s.OpenOrder(&strat.EnterReq{
Tag: "Buy",
Short: false,
})
}
if exitBuyCondition && len(s.LongOrders) > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "ExitBuy",
Dirt: core.OdDirtLong,
})
}
if sellCondition && (tradeDirection == 0 || tradeDirection == 2) {
s.OpenOrder(&strat.EnterReq{
Tag: "Sell",
Short: true,
})
}
if exitSellCondition && len(s.ShortOrders) > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "ExitSell",
Dirt: core.OdDirtShort,
})
}
// 更新状态
state.longStop = longStopVal.Get(0)
state.shortStop = shortStopVal.Get(0)
state.longStopPrev = state.longStop
state.shortStopPrev = state.shortStop
},
}
}
// getMovingAverage 根据类型获取移动平均线
func getMovingAverage(source *ta.Series, maType int, length int) *ta.Series {
switch maType {
case 0: // ema
return ta.EMA(source, length)
case 1: // sma
return ta.SMA(source, length)
case 2: // hma
return ta.HMA(source, length)
case 3: // rma
return ta.RMA(source, length)
case 4: // vwma - 在Banbot中可能没有直接对应,使用SMA代替
return ta.SMA(source, length)
case 5: // wma
return ta.WMA(source, length)
default:
return ta.SMA(source, length)
}
}
// calculateNewHighLows 计算新的高低点条件
func calculateNewHighLows(e *ta.BarEnv, shortPeriod, longPeriod int, consider bool) (bool, bool) {
shortHigh := ta.Highest(e.High, shortPeriod)
longHigh := ta.Highest(e.High, longPeriod)
shortLow := ta.Lowest(e.Low, shortPeriod)
longLow := ta.Lowest(e.Low, longPeriod)
newHigh := true
newLow := true
if consider {
newHigh = math.Abs(shortHigh.Get(0)-longHigh.Get(0)) < 1e-10
newLow = math.Abs(shortLow.Get(0)-longLow.Get(0)) < 1e-10
}
return newHigh, newLow
}
// getYearlyHighLowCondition 获取年度高低点条件
func getYearlyHighLowCondition(e *ta.BarEnv, consider bool) (bool, bool) {
if !consider {
return true, true
}
// 在Banbot中,我们无法直接获取年度数据,所以简化处理
// 使用较长期的高低点来近似年度高低点
yearlyHigh := ta.Highest(e.High, 365)
yearlyLow := ta.Lowest(e.Low, 365)
yearlyHighCondition := e.Close.Get(0) > yearlyHigh.Get(0)
yearlyLowCondition := e.Close.Get(0) < yearlyLow.Get(0)
return yearlyHighCondition, yearlyLowCondition
}