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

227 lines
6.8 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 (
"fmt"
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/core"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/utils"
ta "github.com/banbox/banta"
"math"
)
/*
需要多品种综合信息,品种数不得少于30
做多:RSI超卖、RSI轻度超买跟随
做空:全市场超卖动能枯竭做空
*/
// State845 存储每个品种的RSI动态区间策略状态
type State845 struct {
position int // 当前持仓状态: 0=无持仓, 1=多头, -1=空头
overBoughtDyn float64 // 动态超买水平
overSoldDyn float64 // 动态超卖水平
rsi float64
maSub float64
atr float64
}
// RsiDynamic RSI动态区间反转策略
func J845(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义可调超参数
rsiPeriod := pol.DefInt("rsiPeriod", 12, core.PNorm(7, 21))
overBought := pol.Def("overBought", 76, core.PNorm(70, 90))
overSold := pol.Def("overSold", 26, core.PNorm(20, 40))
sensitivity := pol.Def("sensitivity", 2.5, core.PNorm(1.0, 5.0))
openRate, closeRate := float64(1), float64(0)
prevRsiAvg, prevRsiBig, rsiDelta := float64(0), float64(0), float64(0)
prevOverBuy, prevStamp, prevOpenRate := 0, int64(0), float64(0)
stopLongUntil := int64(0)
dayMS := int64(utils.TFToSecs("1d") * 1000)
return &strat.TradeStrat{
WarmupNum: 91, // 预热K线数量
EachMaxLong: 1, // 最多开1个多单
EachMaxShort: 1, // 最多开1个空单
RunTimeFrames: []string{"1d"},
BatchInOut: true,
OnStartUp: func(s *strat.StratJob) {
// 初始化每个品种的状态
s.More = &State845{
position: 0,
overBoughtDyn: overBought,
overSoldDyn: overSold,
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
state, _ := s.More.(*State845)
rsi := ta.RSI(e.Close, rsiPeriod)
rsiValue := rsi.Get(0)
state.rsi = rsiValue
state.atr = ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
ma20 := ta.SMA(e.Close, 20).Get(0)
ma90 := ta.SMA(e.Close, 90).Get(0)
state.maSub = ma20 - ma90
// 计算动态超买超卖水平
calculateDynamicLevels(rsi, sensitivity, overBought, overSold, state)
// 生成交易信号
if state.position == 0 && openRate > 0 && e.TimeStop >= stopLongUntil { // 无持仓
if rsiValue < state.overSoldDyn {
// RSI跌破超卖水平,建立多头仓位
s.OpenOrder(&strat.EnterReq{
Tag: "long",
CostRate: openRate,
Log: true,
})
state.position = 1
}
} else if state.position == -1 { // 持有空头仓位
if rsiValue <= state.overBoughtDyn {
// RSI从超买区域回落,平掉多头仓位
s.CloseOrders(&strat.ExitReq{
Tag: "exit2",
Dirt: core.OdDirtLong,
EnterTag: "long2",
Log: true,
})
state.position = 0
}
} else if state.position == 1 { // 持有多头仓位
if rsiValue >= state.overSoldDyn {
// RSI从超卖区域回升,平掉多头仓位
s.CloseOrders(&strat.ExitReq{
Tag: "exit1",
Dirt: core.OdDirtLong,
EnterTag: "long",
Log: true,
})
state.position = 0
}
}
},
OnBatchJobs: func(jobs []*strat.StratJob) {
rsiAvg, rsiBig := float64(0), float64(0)
overBuyNum, curStamp := 0, int64(0)
var st *strat.TradeStrat
for _, s := range jobs {
state, _ := s.More.(*State845)
rsiAvg += state.rsi
if state.rsi >= 68 {
rsiBig += 1
}
if state.rsi > state.overBoughtDyn {
overBuyNum += 1
}
curStamp = s.Env.TimeStop
st = s.Strat
}
numFlt := float64(len(jobs))
rsiAvg /= numFlt
rsiBig /= numFlt
if prevRsiAvg >= 70 && rsiAvg < prevRsiAvg && prevRsiBig > 0.5 {
// 极端过热转向,平仓,禁止做多2周
openRate, closeRate = -2, 1
if prevRsiBig > 0.65 && prevOpenRate < 0 {
openRate = -3
stopLongUntil = curStamp + dayMS*14
}
} else if rsiAvg > 77 && rsiBig > 0.75 || rsiAvg > 70 && rsiBig > 0.5 && (rsiAvg-prevRsiAvg)*5 < rsiDelta {
// 严重过热,平仓一半,禁止开仓
openRate, closeRate = -1, 0.5
if curStamp < stopLongUntil {
stopLongUntil = max(stopLongUntil, curStamp+dayMS*10)
}
} else if rsiAvg > 70 && rsiBig > 0.5 {
// 中度过热
openRate, closeRate = 0.6, 0
if curStamp < stopLongUntil {
stopLongUntil = max(stopLongUntil, curStamp+dayMS*6)
}
} else if rsiAvg > 60 && rsiBig > 0.3 {
// 轻度过热
openRate, closeRate = 0.8, 0
} else {
openRate, closeRate = 1, 0
}
if closeRate > 0 {
for _, s := range jobs {
if s.OrderNum > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "cut",
Dirt: core.OdDirtLong,
ExitRate: closeRate,
Log: true,
})
}
}
}
if overBuyNum >= 2 || overBuyNum+prevOverBuy >= 3 {
// 超买跟随,仅适用于多数品种一同上涨,所以要限制数量
for _, s := range jobs {
state, _ := s.More.(*State845)
if state.rsi > state.overBoughtDyn {
// RSI突破超买水平,追涨买入
s.OpenOrder(&strat.EnterReq{
Tag: "long2",
CostRate: openRate,
Log: true,
})
state.position = -1
}
}
}
// 超买做空机制,仅对特定品种表现良好,对于动态品种表现更差
//if openRate <= -3 {
// for _, s := range jobs {
// state, _ := s.More.(*State845)
// if state.maSub < 0 || state.rsi < 76 || len(s.ShortOrders) > 0 {
// continue
// }
// // 牛市中全市场超买时做空;不可在ObBatchJobs中开单,会变差
// s.OpenOrder(&strat.EnterReq{
// Tag: "short1",
// //Short: true,
// TakeProfitVal: state.atr * 2,
// StopLossVal: state.atr * 2,
// Log: true,
// })
// }
//}
if openRate < 1 {
st.WriteOutput(fmt.Sprintf("num: %d, rate: %.3f, rsi: %.3f, rsiBig: %.3f",
len(jobs), openRate, rsiAvg, rsiBig), true)
}
if curStamp > prevStamp {
rsiDelta = rsiAvg - prevRsiAvg
prevRsiAvg = rsiAvg
prevRsiBig = rsiBig
prevStamp = curStamp
prevOpenRate = openRate
prevOverBuy = overBuyNum
}
},
}
}
// calculateDynamicLevels 计算动态超买超卖水平
func calculateDynamicLevels(rsi *ta.Series, sensitivity, overBought, overSold float64, state *State845) {
// 计算近期RSI波动率(标准差)
stdDev, _ := ta.StdDevBy(rsi, 14, 1)
rsiVolatility := stdDev.Get(0)
// 计算动态调整因子
adjustmentFactor := sensitivity * rsiVolatility / 10
// 计算动态超买超卖水平
state.overBoughtDyn = overBought + adjustmentFactor*(100-overBought)/100
state.overSoldDyn = overSold - adjustmentFactor*overSold/100
// 确保动态水平在合理范围内
state.overBoughtDyn = math.Max(70, math.Min(95, state.overBoughtDyn))
state.overSoldDyn = math.Max(5, math.Min(40, state.overSoldDyn))
}