132 lines
3.8 KiB
Go
132 lines
3.8 KiB
Go
package llmjob
|
||
|
||
import (
|
||
"math"
|
||
|
||
"github.com/banbox/banbot/config"
|
||
"github.com/banbox/banbot/core"
|
||
"github.com/banbox/banbot/orm/ormo"
|
||
"github.com/banbox/banbot/strat"
|
||
ta "github.com/banbox/banta"
|
||
)
|
||
|
||
func J7046(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||
// 定义可调超参数
|
||
smaPeriod := pol.DefInt("smaPeriod", 22, core.PNorm(10, 50))
|
||
mult := pol.Def("mult", 1.0, core.PNorm(0.8, 3.0))
|
||
trailingLossPct := pol.Def("trailingLossPct", 0.075, core.PNorm(0.03, 0.15))
|
||
trailingPointsPct := pol.Def("trailingPointsPct", 0.05, core.PNorm(0.01, 0.10))
|
||
trailingOffsetPct := pol.Def("trailingOffsetPct", 0.005, core.PNorm(0.001, 0.02))
|
||
|
||
return &strat.TradeStrat{
|
||
Name: "Bollinger Band Breakout",
|
||
WarmupNum: smaPeriod + 10, // 预热K线数量
|
||
HedgeOff: true, // 单向持仓模式
|
||
EachMaxLong: 1, // 最多1个多单
|
||
EachMaxShort: 1, // 最多1个空单
|
||
TimeFrames: "8h",
|
||
OnBar: func(s *strat.StratJob) {
|
||
e := s.Env
|
||
closePrice := e.Close.Get(0)
|
||
|
||
// 检查数据有效性
|
||
if math.IsNaN(closePrice) {
|
||
return
|
||
}
|
||
|
||
// 计算布林带指标
|
||
upper, _, lower := ta.BBANDS(e.Close, smaPeriod, mult, mult)
|
||
|
||
// 检测突破信号
|
||
longSignal := e.Close.Cross(upper) // 上穿上轨,返回1表示上穿
|
||
shortSignal := e.Close.Cross(lower) // 下穿下轨,返回-1表示下穿
|
||
|
||
// 多头入场逻辑
|
||
if longSignal == 1 && len(s.LongOrders) == 0 {
|
||
s.OpenOrder(&strat.EnterReq{
|
||
Tag: "long",
|
||
Short: false,
|
||
OrderType: core.OrderTypeMarket,
|
||
})
|
||
}
|
||
|
||
// 空头入场逻辑
|
||
if shortSignal == -1 && len(s.ShortOrders) == 0 {
|
||
s.OpenOrder(&strat.EnterReq{
|
||
Tag: "short",
|
||
Short: true,
|
||
OrderType: core.OrderTypeMarket,
|
||
})
|
||
}
|
||
|
||
// 更新追踪止损
|
||
updateTrailingStop(s, closePrice, trailingLossPct, trailingPointsPct, trailingOffsetPct)
|
||
},
|
||
}
|
||
}
|
||
|
||
// 更新追踪止损的辅助函数
|
||
func updateTrailingStop(s *strat.StratJob, closePrice, lossPct, pointsPct, offsetPct float64) {
|
||
// 处理多头仓位的追踪止损
|
||
for _, order := range s.LongOrders {
|
||
if order.Status >= 2 { // 已入场
|
||
entryPrice := order.Enter.Average
|
||
maxLossPrice := entryPrice * (1 - lossPct) // 最大损失价格
|
||
activationPrice := entryPrice * (1 + pointsPct) // 激活追踪的价格
|
||
|
||
// 获取当前止损信息
|
||
currentStop := order.GetStopLoss()
|
||
|
||
var newStopPrice float64
|
||
|
||
// 判断是否已激活追踪止损
|
||
if closePrice >= activationPrice {
|
||
// 已激活,使用偏移量计算止损
|
||
newStopPrice = closePrice * (1 - offsetPct)
|
||
} else {
|
||
// 未激活,使用最大损失作为止损
|
||
newStopPrice = maxLossPrice
|
||
}
|
||
|
||
// 只有当新止损价格更优(更高)时才更新
|
||
if currentStop == nil || newStopPrice > currentStop.Price {
|
||
order.SetStopLoss(&ormo.ExitTrigger{
|
||
Price: newStopPrice,
|
||
Tag: "trailing_stop",
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理空头仓位的追踪止损
|
||
for _, order := range s.ShortOrders {
|
||
if order.Status >= 2 { // 已入场
|
||
entryPrice := order.Enter.Average
|
||
maxLossPrice := entryPrice * (1 + lossPct) // 最大损失价格
|
||
activationPrice := entryPrice * (1 - pointsPct) // 激活追踪的价格
|
||
|
||
// 获取当前止损信息
|
||
currentStop := order.GetStopLoss()
|
||
|
||
var newStopPrice float64
|
||
|
||
// 判断是否已激活追踪止损
|
||
if closePrice <= activationPrice {
|
||
// 已激活,使用偏移量计算止损
|
||
newStopPrice = closePrice * (1 + offsetPct)
|
||
} else {
|
||
// 未激活,使用最大损失作为止损
|
||
newStopPrice = maxLossPrice
|
||
}
|
||
|
||
// 只有当新止损价格更优(更低)时才更新
|
||
if currentStop == nil || newStopPrice < currentStop.Price {
|
||
order.SetStopLoss(&ormo.ExitTrigger{
|
||
Price: newStopPrice,
|
||
Tag: "trailing_stop",
|
||
})
|
||
}
|
||
}
|
||
}
|
||
}
|