114 lines
3.2 KiB
Go
114 lines
3.2 KiB
Go
package llmjob
|
|
|
|
import (
|
|
"github.com/banbox/banbot/config"
|
|
"github.com/banbox/banbot/core"
|
|
"github.com/banbox/banbot/strat"
|
|
ta "github.com/banbox/banta"
|
|
"math"
|
|
)
|
|
|
|
func J3992(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
|
// 定义可调超参数
|
|
fastLength := pol.DefInt("fastLength", 10, core.PUniform(5, 20))
|
|
macdSlowRate := pol.Def("macdSlowRate", 2.17, core.PNorm(1.5, 4))
|
|
rsiThreshold := pol.DefInt("rsiThreshold", 65, core.PUniform(60, 80))
|
|
bbMult := pol.Def("bbMult", 2.0, core.PNorm(1.5, 3.0))
|
|
rsiLength := pol.DefInt("rsiLength", 14, core.PUniform(7, 21))
|
|
|
|
// 计算其他参数
|
|
MACDLength := 9 // 固定值
|
|
slowLength := int(float64(fastLength) * macdSlowRate)
|
|
rsiOverbought := float64(rsiThreshold)
|
|
rsiOversold := 100 - rsiOverbought
|
|
stopLossPercent := 0.02 // 2% stop loss
|
|
takeProfitPercent := 0.05 // 5% take profit
|
|
|
|
// 计算预热数量,取最大值
|
|
warmupNum := slowLength
|
|
if MACDLength > warmupNum {
|
|
warmupNum = MACDLength
|
|
}
|
|
if rsiLength > warmupNum {
|
|
warmupNum = rsiLength
|
|
}
|
|
if bbLength := 20; bbLength > warmupNum {
|
|
warmupNum = bbLength
|
|
}
|
|
|
|
return &strat.TradeStrat{
|
|
WarmupNum: warmupNum,
|
|
HedgeOff: true, // 单向持仓模式
|
|
RunTimeFrames: []string{"8h"},
|
|
OnBar: func(s *strat.StratJob) {
|
|
e := s.Env
|
|
|
|
// 检查数据长度是否足够
|
|
if e.Close.Len() < warmupNum {
|
|
return
|
|
}
|
|
|
|
// MACD calculations
|
|
fastMA := ta.EMA(e.Close, fastLength)
|
|
slowMA := ta.EMA(e.Close, slowLength)
|
|
macdLine := fastMA.Sub(slowMA)
|
|
signalLine := ta.EMA(macdLine, MACDLength)
|
|
|
|
// RSI calculation
|
|
rsi := ta.RSI(e.Close, rsiLength)
|
|
|
|
// Bollinger Bands calculation
|
|
bbLength := 20
|
|
basis := ta.SMA(e.Close, bbLength)
|
|
stdDev := ta.StdDev(e.Close, bbLength)
|
|
dev := stdDev.Mul(bbMult)
|
|
upper := basis.Add(dev)
|
|
lower := basis.Sub(dev)
|
|
|
|
// 获取当前值
|
|
closePrice := e.Close.Get(0)
|
|
macdVal := macdLine.Get(0)
|
|
signalVal := signalLine.Get(0)
|
|
rsiVal := rsi.Get(0)
|
|
upperVal := upper.Get(0)
|
|
lowerVal := lower.Get(0)
|
|
|
|
// 检查NaN值
|
|
if math.IsNaN(closePrice) || math.IsNaN(macdVal) || math.IsNaN(signalVal) ||
|
|
math.IsNaN(rsiVal) || math.IsNaN(upperVal) || math.IsNaN(lowerVal) {
|
|
return
|
|
}
|
|
|
|
// Entry conditions
|
|
macdCrossOver := macdLine.Cross(signalLine) == 1
|
|
rsiCrossUnder := rsi.Cross(rsiOversold) == -1
|
|
longCondition := (macdCrossOver || rsiCrossUnder) && closePrice > lowerVal
|
|
|
|
macdCrossUnder := macdLine.Cross(signalLine) == -1
|
|
rsiCrossOver := rsi.Cross(rsiOverbought) == 1
|
|
shortCondition := (macdCrossUnder || rsiCrossOver) && closePrice < upperVal
|
|
|
|
// Long position logic
|
|
if longCondition && len(s.LongOrders) == 0 {
|
|
s.OpenOrder(&strat.EnterReq{
|
|
Tag: "Long",
|
|
OrderType: core.OrderTypeMarket,
|
|
StopLossVal: closePrice * (1 - stopLossPercent),
|
|
TakeProfitVal: closePrice * (1 + takeProfitPercent),
|
|
})
|
|
}
|
|
|
|
// Short position logic
|
|
if shortCondition && len(s.ShortOrders) == 0 {
|
|
s.OpenOrder(&strat.EnterReq{
|
|
Tag: "Short",
|
|
Short: true,
|
|
OrderType: core.OrderTypeMarket,
|
|
StopLossVal: closePrice * (1 + stopLossPercent),
|
|
TakeProfitVal: closePrice * (1 - takeProfitPercent),
|
|
})
|
|
}
|
|
},
|
|
}
|
|
}
|