227 lines
6.8 KiB
Go
227 lines
6.8 KiB
Go
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))
|
||
}
|