This commit is contained in:
2026-08-24 20:53:21 +08:00
commit 6e78bba16a
287 changed files with 32443 additions and 0 deletions

624
llmjob/7699.go Normal file
View File

@@ -0,0 +1,624 @@
package llmjob
import (
"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"
)
type HullBusterState struct {
// 交易状态
entryPrice float64
stopPrice float64
lastLevel float64
maeLevel float64
pyramidCount int
pyramidWaveID int
entryTrendDir int
consecLosses int
// 交叉跟踪
riseALCrossPrice float64
riseALNetChg float64
fallALCrossPrice float64
fallALNetChg float64
riseALBarsSince int
fallALBarsSince int
// EMA交叉跟踪
riseEMCrossPrice float64
riseEMNetChg float64
fallEMCrossPrice float64
fallEMNetChg float64
riseEMBarsSince int
fallEMBarsSince int
// ZScore跟踪
riseZSSince int
fallZSSince int
riseZSCount int
fallZSCount int
riseZSHigh float64
fallZSLow float64
aboveZSSince int
belowZSSince int
aboveZSCount int
belowZSCount int
// 事件状态
htfEventHigh float64
htfEventLow float64
lastHTFEvent int
impulseWaveDir int
impulseWaveID int
// 状态标志
nascentEntry bool
hemiTransit bool
feeRise bool
feeFall bool
pullback bool
}
func J7699(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 可调参数定义
fastCrossLen := pol.DefInt("fastCrossLen", 9, core.PNorm(5, 20))
smaCrossRate := pol.Def("smaCrossRate", 2.6, core.PNorm(1.5, 5.0))
trendDEMALen := pol.DefInt("trendDEMALen", 200, core.PNorm(50, 300))
fastAlmaLen := pol.DefInt("fastAlmaLen", 10, core.PNorm(5, 30))
almaLinregRate := pol.Def("almaLinregRate", 3.0, core.PNorm(2.0, 7.0))
// 固定参数
slowCrossLen := int(float64(fastCrossLen) * smaCrossRate)
slowLinRegLen := int(float64(fastAlmaLen) * almaLinregRate)
fastLinRegLen := 30
fastEMALen := 8
stochastLen := 14
minProfit := 0.50
stopOffset := 0.50
maxAddOns := 0
pyramidSpan := 0.0
tickScaler := 10.0
chgDivisor := 1000.0
// 计算所需预热长度
warmupNum := fastCrossLen
if slowCrossLen > warmupNum {
warmupNum = slowCrossLen
}
if trendDEMALen > warmupNum {
warmupNum = trendDEMALen
}
if fastAlmaLen > warmupNum {
warmupNum = fastAlmaLen
}
if slowLinRegLen > warmupNum {
warmupNum = slowLinRegLen
}
if fastLinRegLen > warmupNum {
warmupNum = fastLinRegLen
}
if fastEMALen > warmupNum {
warmupNum = fastEMALen
}
if stochastLen > warmupNum {
warmupNum = stochastLen
}
warmupNum += 100 // 为ZScore等计算预留空间
return &strat.TradeStrat{
WarmupNum: warmupNum,
HedgeOff: true, // 单向持仓
TimeFrames: "8h",
OnStartUp: func(s *strat.StratJob) {
s.More = &HullBusterState{}
},
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
// 需要两个高时间框架:信号周期和趋势周期
return []*strat.PairSub{
{"_cur_", "1h", warmupNum}, // 信号周期
{"_cur_", "4h", warmupNum}, // 趋势周期
}
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair string, tf string) {
// 处理高时间框架数据
if tf == "1h" {
// 将1h数据存储到环境中
s.Env.Data.Store("_cur_1h", e)
} else if tf == "4h" {
// 将4h数据存储到环境中
s.Env.Data.Store("_cur_4h", e)
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
state, _ := s.More.(*HullBusterState)
// 当前周期指标计算
fastCross := ta.SMA(e.Close, fastCrossLen)
slowCross := ta.SMA(e.Close, slowCrossLen)
fastEMA := ta.EMA(e.Close, fastEMALen)
// 使用Stochastic指标而非KDJ
fastK := Stochastic(e.High, e.Low, e.Close, stochastLen)
slowD := ta.SMA(fastK, 3)
// 交叉信号 - 只计算一次
crossValue := fastCross.Cross(slowCross)
crossRise := crossValue == 1
crossFall := crossValue == -1
// 从高时间框架获取数据
htfSignalData, _ := s.Env.Data.Load("_cur_1h")
htfTrendData, _ := s.Env.Data.Load("_cur_4h")
if htfSignalData == nil || htfTrendData == nil {
return
}
signalEnv := htfSignalData.(*ta.BarEnv)
trendEnv := htfTrendData.(*ta.BarEnv)
// HTF指标
slowLinReg := ta.LinReg(trendEnv.Close, slowLinRegLen)
fastLinReg := ta.LinReg(signalEnv.Close, fastLinRegLen)
fastAlma := ta.ALMA(signalEnv.Close, fastAlmaLen, 6.0, 0.85)
trendDEMA := DEMA(trendEnv.Close, trendDEMALen)
// ZScore计算
almaMean := ta.SMA(fastAlma, 100)
almaMarks := fastAlma.Sub(almaMean)
almaStdDev := ta.StdDev(almaMarks, 100)
zScore := almaMarks.Div(almaStdDev)
// HTF Stochastic
htfFastK := Stochastic(trendEnv.High, trendEnv.Low, trendEnv.Close, stochastLen)
htfDLine := ta.SMA(htfFastK, 3)
htfSlowD := ta.SMA(htfDLine, 3)
// 信号条件 - 使用Get(1)获取前一根K线值以对齐HTF数据
closePrice := e.Close.Get(0)
trendDEMAVal := trendDEMA.Get(1)
fastAlmaVal := fastAlma.Get(1)
fastLinRegVal := fastLinReg.Get(1)
slowLinRegVal := slowLinReg.Get(1)
zScoreVal := zScore.Get(1)
// 趋势信号
htfSignalRise := fastAlmaVal > fastLinRegVal
htfSignalFall := fastAlmaVal < fastLinRegVal
htfFastAboveTrend := fastAlmaVal > trendDEMAVal && fastLinRegVal > trendDEMAVal
htfFastBelowTrend := fastAlmaVal < trendDEMAVal && fastLinRegVal < trendDEMAVal
// 计算ALMA事件
updateALEvents(state, fastAlmaVal, fastLinRegVal, closePrice, tickScaler, chgDivisor)
// 计算EMA事件
fastEMAVal := fastEMA.Get(0)
updateEMEvents(state, fastEMAVal, fastAlmaVal, closePrice, tickScaler, chgDivisor)
// 更新ZScore状态
updateZScoreState(state, zScoreVal, closePrice)
// 更新脉冲波方向
waveDirNow := 0
if fastLinRegVal > slowLinRegVal {
waveDirNow = 1
} else if fastLinRegVal < slowLinRegVal {
waveDirNow = -1
}
if waveDirNow != state.impulseWaveDir {
state.impulseWaveDir = waveDirNow
state.impulseWaveID++
}
// 计算事件
riseALEvent := checkRiseEvent(state, 10.0, 0.1)
fallALEvent := checkFallEvent(state, 10.0, 0.1)
riseEMEvent := checkRiseEMEvent(state, 3.0, 0.8)
fallEMEvent := checkFallEMEvent(state, 4.0, 0.8)
// Stochastic事件
fullFanCone := fastEMAVal > fastAlmaVal && fastAlmaVal > fastLinRegVal && fastLinRegVal > slowLinRegVal
downFanCone := fastEMAVal < fastAlmaVal && fastAlmaVal < fastLinRegVal && fastLinRegVal < slowLinRegVal
partialTrendRise := fastEMAVal > fastAlmaVal && fastAlmaVal > slowLinRegVal && fastLinRegVal > slowLinRegVal
partialTrendFall := fastEMAVal < fastAlmaVal && fastAlmaVal < slowLinRegVal && fastLinRegVal < slowLinRegVal
durableTrendRise := partialTrendRise && slowLinRegVal > trendDEMAVal
durableTrendFall := partialTrendFall && slowLinRegVal < trendDEMAVal
riseStochas := (htfDLine.Get(1) > 80 && fullFanCone) ||
(htfSlowD.Get(1) > 90 && durableTrendRise) ||
(htfDLine.Get(1) > 90 && partialTrendRise && state.htfEventHigh > 0 && closePrice > state.htfEventHigh)
fallStochas := (htfDLine.Get(1) < 20 && downFanCone) ||
(htfSlowD.Get(1) < 10 && durableTrendFall) ||
(htfDLine.Get(1) < 10 && partialTrendFall && state.htfEventLow > 0 && closePrice < state.htfEventLow)
// 更新HTF事件高低点
if riseALEvent {
state.htfEventHigh = closePrice
state.lastHTFEvent = 1
}
if fallALEvent {
state.htfEventLow = closePrice
state.lastHTFEvent = -1
}
// Nascent Entry条件
htfNascentRise := fastAlmaVal > slowLinRegVal && fastLinRegVal > slowLinRegVal && slowLinRegVal < trendDEMAVal
htfNascentFall := fastAlmaVal < slowLinRegVal && fastLinRegVal < slowLinRegVal && slowLinRegVal > trendDEMAVal
// 检查是否有持仓
positionDir := 0
if len(s.LongOrders) > 0 {
positionDir = 1
} else if len(s.ShortOrders) > 0 {
positionDir = -1
}
alreadyIn := positionDir != 0
// 计算盈利状态
var currentOrder *ormo.InOutOrder
if len(s.LongOrders) > 0 {
currentOrder = s.LongOrders[0]
} else if len(s.ShortOrders) > 0 {
currentOrder = s.ShortOrders[0]
}
profitable := false
if currentOrder != nil {
profitAmount := currentOrder.ProfitRate * currentOrder.Leverage * state.entryPrice
profitable = profitAmount >= minProfit
}
// 更新ZScore退出条件
if alreadyIn {
if zScoreVal > 5.0 {
state.feeRise = true
}
if zScoreVal < -5.0 {
state.feeFall = true
}
}
// 入场逻辑(包含Nascent Entry)
if !alreadyIn {
if crossRise {
longNascent := false
if closePrice <= trendDEMAVal && htfNascentRise {
longNascent = true
state.nascentEntry = true
}
if closePrice > trendDEMAVal || longNascent {
stopPrice := e.Low.Get(0) - stopOffset
s.OpenOrder(&strat.EnterReq{
Tag: "WBHDrive-L",
Short: false,
StopLoss: stopPrice,
StopLossTag: "stop_loss",
})
state.entryPrice = closePrice
state.stopPrice = stopPrice
state.lastLevel = closePrice
state.entryTrendDir = 1
if !longNascent {
state.nascentEntry = false
}
}
} else if crossFall {
shortNascent := false
if closePrice >= trendDEMAVal && htfNascentFall {
shortNascent = true
state.nascentEntry = true
}
if closePrice < trendDEMAVal || shortNascent {
stopPrice := e.High.Get(0) + stopOffset
s.OpenOrder(&strat.EnterReq{
Tag: "WBHDrive-S",
Short: true,
StopLoss: stopPrice,
StopLossTag: "stop_loss",
})
state.entryPrice = closePrice
state.stopPrice = stopPrice
state.lastLevel = closePrice
state.entryTrendDir = -1
if !shortNascent {
state.nascentEntry = false
}
}
}
}
// 出场逻辑
if alreadyIn && profitable {
shouldClose := false
if positionDir == 1 { // 多头
// 交叉退出
if htfSignalFall {
shouldClose = true
}
// 信号退出
if htfSignalRise && riseALEvent && state.feeRise {
shouldClose = true
}
// 事件退出
if riseEMEvent && fastEMAVal > fastAlmaVal && htfFastAboveTrend {
shouldClose = true
}
// Stochastic退出
if riseStochas {
shouldClose = true
}
} else if positionDir == -1 { // 空头
// 交叉退出
if htfSignalRise {
shouldClose = true
}
// 信号退出
if htfSignalFall && fallALEvent && state.feeFall {
shouldClose = true
}
// 事件退出
if fallEMEvent && fastEMAVal < fastAlmaVal && htfFastBelowTrend {
shouldClose = true
}
// Stochastic退出
if fallStochas {
shouldClose = true
}
}
if shouldClose {
if positionDir == 1 {
s.CloseOrders(&strat.ExitReq{
Tag: "exit_long",
Dirt: core.OdDirtLong,
})
} else {
s.CloseOrders(&strat.ExitReq{
Tag: "exit_short",
Dirt: core.OdDirtShort,
})
}
// 重置状态
if profitable {
state.consecLosses = 0
} else {
state.consecLosses++
}
state.entryPrice = 0
state.stopPrice = 0
state.lastLevel = 0
state.pyramidCount = 0
state.feeRise = false
state.feeFall = false
}
}
// 完整的金字塔加仓逻辑
if alreadyIn && maxAddOns > 0 && state.consecLosses < maxAddOns {
// 检测pullback条件
if positionDir == 1 && slowD.Get(0) < 20 {
if fallALEvent || fallStochas {
state.pullback = true
}
} else if positionDir == -1 && slowD.Get(0) > 80 {
if riseALEvent || riseStochas {
state.pullback = true
}
}
shouldPyramid := false
if positionDir == 1 {
// 完整的多头金字塔逻辑
exceedingSpan := pyramidSpan > 0 && (state.lastLevel-closePrice) > pyramidSpan
if (crossRise && closePrice > trendDEMAVal && htfFastAboveTrend && closePrice > state.lastLevel && state.pullback) ||
(closePrice < state.lastLevel && exceedingSpan && closePrice > state.entryPrice && (fallALEvent || fallEMEvent || fallStochas)) ||
(closePrice < state.lastLevel && exceedingSpan && closePrice < state.entryPrice && fallALEvent && fallStochas && state.impulseWaveID > state.pyramidWaveID) {
shouldPyramid = true
}
} else if positionDir == -1 {
// 完整的空头金字塔逻辑
exceedingSpan := pyramidSpan > 0 && (closePrice-state.lastLevel) > pyramidSpan
if (crossFall && closePrice < trendDEMAVal && htfFastBelowTrend && closePrice < state.lastLevel && state.pullback) ||
(closePrice > state.lastLevel && exceedingSpan && closePrice < state.entryPrice && (riseALEvent || riseEMEvent || riseStochas)) ||
(closePrice > state.lastLevel && exceedingSpan && closePrice > state.entryPrice && riseALEvent && riseStochas && state.impulseWaveID > state.pyramidWaveID) {
shouldPyramid = true
}
}
if shouldPyramid {
if positionDir == 1 {
stopPrice := e.Low.Get(0) - stopOffset
s.OpenOrder(&strat.EnterReq{
Tag: "WBHDrive-L-Pyramid",
Short: false,
StopLoss: stopPrice,
StopLossTag: "stop_loss",
})
} else {
stopPrice := e.High.Get(0) + stopOffset
s.OpenOrder(&strat.EnterReq{
Tag: "WBHDrive-S-Pyramid",
Short: true,
StopLoss: stopPrice,
StopLossTag: "stop_loss",
})
}
state.pyramidCount++
state.pyramidWaveID = state.impulseWaveID
state.lastLevel = closePrice
state.pullback = false
}
}
},
}
}
// 实现与PineScript stoch()函数一致的Stochastic指标
func Stochastic(high, low, close *ta.Series, length int) *ta.Series {
llowest := ta.Lowest(low, length)
hhighest := ta.Highest(high, length)
k := close.Sub(llowest).Div(hhighest.Sub(llowest)).Mul(100)
return k
}
// 辅助函数:更新ALMA事件状态(包含tick标准化)
func updateALEvents(state *HullBusterState, fastAlma, fastLinReg, closePrice, tickScaler, chgDivisor float64) {
fxMinTick := tickScaler
if fastAlma > fastLinReg {
if state.fallALBarsSince > 0 { // 从下跌转为上涨
state.fallALBarsSince = 0
state.fallALCrossPrice = 0
state.riseALBarsSince = 1
state.riseALCrossPrice = closePrice
state.riseALNetChg = 0
} else {
state.riseALBarsSince++
if state.riseALCrossPrice > 0 {
divisor := chgDivisor
if chgDivisor <= 1.0 {
divisor = state.riseALCrossPrice
}
state.riseALNetChg = ((closePrice - state.riseALCrossPrice) / fxMinTick / divisor) * 100.0
}
}
} else if fastAlma < fastLinReg {
if state.riseALBarsSince > 0 { // 从上涨转为下跌
state.riseALBarsSince = 0
state.riseALCrossPrice = 0
state.fallALBarsSince = 1
state.fallALCrossPrice = closePrice
state.fallALNetChg = 0
} else {
state.fallALBarsSince++
if state.fallALCrossPrice > 0 {
divisor := chgDivisor
if chgDivisor <= 1.0 {
divisor = state.fallALCrossPrice
}
state.fallALNetChg = ((state.fallALCrossPrice - closePrice) / fxMinTick / divisor) * 100.0
}
}
}
}
// 辅助函数:更新EMA事件状态(包含tick标准化)
func updateEMEvents(state *HullBusterState, fastEMA, fastAlma, closePrice, tickScaler, chgDivisor float64) {
fxMinTick := tickScaler
if fastEMA > fastAlma {
if state.fallEMBarsSince > 0 {
state.fallEMBarsSince = 0
state.fallEMCrossPrice = 0
state.riseEMBarsSince = 1
state.riseEMCrossPrice = closePrice
state.riseEMNetChg = 0
} else {
state.riseEMBarsSince++
if state.riseEMCrossPrice > 0 {
divisor := chgDivisor
if chgDivisor <= 1.0 {
divisor = state.riseEMCrossPrice
}
state.riseEMNetChg = ((closePrice - state.riseEMCrossPrice) / fxMinTick / divisor) * 100.0
}
}
} else if fastEMA < fastAlma {
if state.riseEMBarsSince > 0 {
state.riseEMBarsSince = 0
state.riseEMCrossPrice = 0
state.fallEMBarsSince = 1
state.fallEMCrossPrice = closePrice
state.fallEMNetChg = 0
} else {
state.fallEMBarsSince++
if state.fallEMCrossPrice > 0 {
divisor := chgDivisor
if chgDivisor <= 1.0 {
divisor = state.fallEMCrossPrice
}
state.fallEMNetChg = ((state.fallEMCrossPrice - closePrice) / fxMinTick / divisor) * 100.0
}
}
}
}
// 辅助函数:更新ZScore状态
func updateZScoreState(state *HullBusterState, zScore, closePrice float64) {
state.aboveZSSince++
state.belowZSSince++
state.riseZSSince++
state.fallZSSince++
// 简化的ZScore跟踪
if zScore > 0 {
if state.aboveZSSince > 0 {
state.aboveZSSince = 0
state.aboveZSCount = 1
} else {
state.aboveZSCount++
}
}
if zScore < 0 {
if state.belowZSSince > 0 {
state.belowZSSince = 0
state.belowZSCount = 1
} else {
state.belowZSCount++
}
}
}
// 辅助函数:检查上升事件
func checkRiseEvent(state *HullBusterState, minNetChg, minROC float64) bool {
return state.riseALBarsSince > 0 &&
state.riseALNetChg > 0 &&
state.riseALNetChg >= minNetChg &&
(state.riseALNetChg/float64(state.riseALBarsSince)) >= minROC
}
// 辅助函数:检查下降事件
func checkFallEvent(state *HullBusterState, minNetChg, minROC float64) bool {
return state.fallALBarsSince > 0 &&
state.fallALNetChg > 0 &&
state.fallALNetChg >= minNetChg &&
(state.fallALNetChg/float64(state.fallALBarsSince)) >= minROC
}
// 辅助函数:检查EMA上升事件
func checkRiseEMEvent(state *HullBusterState, minNetChg, minROC float64) bool {
return state.riseEMBarsSince > 0 &&
state.riseEMNetChg > 0 &&
state.riseEMNetChg >= minNetChg &&
(state.riseEMNetChg/float64(state.riseEMBarsSince)) >= minROC
}
// 辅助函数:检查EMA下降事件
func checkFallEMEvent(state *HullBusterState, minNetChg, minROC float64) bool {
return state.fallEMBarsSince > 0 &&
state.fallEMNetChg > 0 &&
state.fallEMNetChg >= minNetChg &&
(state.fallEMNetChg/float64(state.fallEMBarsSince)) >= minROC
}
// DEMA实现(双重指数移动平均)
func DEMA(series *ta.Series, period int) *ta.Series {
ema1 := ta.EMA(series, period)
ema2 := ta.EMA(ema1, period)
return ema1.Mul(2).Sub(ema2)
}