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

216 lines
6.0 KiB
Go
Raw Permalink 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.

package llmjob
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
ta "github.com/banbox/banta"
"math"
)
// State5609 存储每个品种的策略状态
type State5609 struct {
longEntryPrice float64
shortEntryPrice float64
longEntryBar int64
shortEntryBar int64
}
func J5609(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义可调超参数
atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 30))
bbLength := pol.DefInt("bbLength", 18, core.PNorm(10, 50))
bbStdDev := pol.Def("bbStdDev", 1.9, core.PNorm(1, 3))
rsiPeriod := pol.DefInt("rsiPeriod", 12, core.PNorm(7, 21))
atrMultiplier := pol.Def("atrMultiplier", 1.6, core.PNorm(0.5, 3))
vwapLength := pol.DefInt("vwapLength", 20, core.PNorm(10, 40))
targetMultiplier := pol.Def("targetMultiplier", 2.4, core.PNorm(1, 4))
maxHoldingBars := pol.DefInt("maxHoldingBars", 3, core.PNorm(2, 10))
// 计算预热期,确保所有指标都有足够的数据计算
warmupNum := bbLength
if atrPeriod > warmupNum {
warmupNum = atrPeriod
}
if rsiPeriod > warmupNum {
warmupNum = rsiPeriod
}
if vwapLength > warmupNum {
warmupNum = vwapLength
}
// 额外增加20个K线确保稳定性
warmupNum += 20
return &strat.TradeStrat{
Name: "OptionsStrategy",
WarmupNum: warmupNum,
HedgeOff: true, // 单向持仓模式
EachMaxLong: 1, // 最多一个多头订单
EachMaxShort: 1, // 最多一个空头订单
RunTimeFrames: []string{"4h"},
OnStartUp: func(s *strat.StratJob) {
// 初始化每个品种的策略状态
s.More = &State5609{
longEntryPrice: 0,
shortEntryPrice: 0,
longEntryBar: 0,
shortEntryBar: 0,
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
state := s.More.(*State5609)
// 计算技术指标
atrValue := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
smaValue := ta.SMA(e.Close, bbLength)
stdDev := ta.StdDev(e.Close, bbLength)
upperBB := smaValue.Add(stdDev.Mul(bbStdDev))
lowerBB := smaValue.Sub(stdDev.Mul(bbStdDev))
rsiValue := ta.RSI(e.Close, rsiPeriod)
// 修复错误1:使用正确的VWAP计算方法
vwap := ta.Sum(e.Close.Mul(e.Volume), vwapLength).Div(ta.Sum(e.Volume, vwapLength))
volSMA := ta.SMA(e.Volume, 10)
atrSMA := ta.SMA(atrValue, 20)
// 获取当前值
closePrice := e.Close.Get(0)
atrVal := atrValue.Get(0)
upperBBVal := upperBB.Get(0)
lowerBBVal := lowerBB.Get(0)
rsiVal := rsiValue.Get(0)
vwapVal := vwap.Get(0)
volumeVal := e.Volume.Get(0)
volSMAVal := volSMA.Get(0)
atrSMAVal := atrSMA.Get(0)
// 检查数据有效性
if math.IsNaN(closePrice) || math.IsNaN(atrVal) || math.IsNaN(upperBBVal) ||
math.IsNaN(lowerBBVal) || math.IsNaN(rsiVal) || math.IsNaN(vwapVal) ||
math.IsNaN(volumeVal) || math.IsNaN(volSMAVal) || math.IsNaN(atrSMAVal) {
return
}
// 计算条件
volSpike := volumeVal > volSMAVal*1.5
atrFilter := atrVal > atrSMAVal*0.5
// 生成交易信号
longCE := e.Close.Cross(upperBB) == 1 && rsiVal > 60 && volSpike && closePrice > vwapVal && atrFilter
longPE := e.Close.Cross(lowerBB) == -1 && rsiVal < 40 && volSpike && closePrice < vwapVal && atrFilter
// 计算止损止盈
longStopLoss := 0.0
longTarget := 0.0
shortStopLoss := 0.0
shortTarget := 0.0
// 修复错误5:使用实际成交的平均价格作为止损止盈的计算基准
if len(s.LongOrders) > 0 {
// 获取多头持仓的平均价格
longAvgPrice := state.longEntryPrice
if longAvgPrice > 0 {
longStopLoss = longAvgPrice - atrMultiplier*atrVal
longTarget = longAvgPrice + targetMultiplier*atrVal
}
}
if len(s.ShortOrders) > 0 {
// 获取空头持仓的平均价格
shortAvgPrice := state.shortEntryPrice
if shortAvgPrice > 0 {
shortStopLoss = shortAvgPrice + atrMultiplier*atrVal
shortTarget = shortAvgPrice - targetMultiplier*atrVal
}
}
// 执行交易
if longCE && len(s.LongOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "CE Entry",
OrderType: core.OrderTypeMarket,
StopLossVal: atrMultiplier * atrVal,
TakeProfitVal: targetMultiplier * atrVal,
})
// 记录入场价格和K线时间
state.longEntryPrice = closePrice
state.longEntryBar = e.TimeStop
}
if longPE && len(s.ShortOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "PE Entry",
Short: true,
OrderType: core.OrderTypeMarket,
StopLossVal: atrMultiplier * atrVal,
TakeProfitVal: targetMultiplier * atrVal,
})
// 记录入场价格和K线时间
state.shortEntryPrice = closePrice
state.shortEntryBar = e.TimeStop
}
// 出场管理
if len(s.LongOrders) > 0 {
// 计算持仓时间(K线数)
holdBars := e.BarCount(state.longEntryBar)
// 止盈出场
if closePrice >= longTarget {
s.CloseOrders(&strat.ExitReq{
Tag: "CE Target Hit",
Dirt: core.OdDirtLong,
Force: true,
})
}
// 止损出场
if closePrice <= longStopLoss {
s.CloseOrders(&strat.ExitReq{
Tag: "CE Stop Loss Hit",
Dirt: core.OdDirtLong,
Force: true,
})
}
// 时间出场
if int64(holdBars) >= int64(maxHoldingBars) {
s.CloseOrders(&strat.ExitReq{
Tag: "CE Timed Exit",
Dirt: core.OdDirtLong,
Force: true,
})
}
}
if len(s.ShortOrders) > 0 {
// 计算持仓时间(K线数)
holdBars := e.BarCount(state.shortEntryBar)
// 止盈出场
if closePrice <= shortTarget {
s.CloseOrders(&strat.ExitReq{
Tag: "PE Target Hit",
Dirt: core.OdDirtShort,
Force: true,
})
}
// 止损出场
if closePrice >= shortStopLoss {
s.CloseOrders(&strat.ExitReq{
Tag: "PE Stop Loss Hit",
Dirt: core.OdDirtShort,
Force: true,
})
}
// 时间出场
if int64(holdBars) >= int64(maxHoldingBars) {
s.CloseOrders(&strat.ExitReq{
Tag: "PE Timed Exit",
Dirt: core.OdDirtShort,
Force: true,
})
}
}
},
}
}