216 lines
6.0 KiB
Go
216 lines
6.0 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"
|
||
)
|
||
|
||
// 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,
|
||
})
|
||
}
|
||
}
|
||
},
|
||
}
|
||
}
|