269 lines
8.7 KiB
Go
269 lines
8.7 KiB
Go
// 修复后的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
|
||
}
|