init
This commit is contained in:
624
llmjob/7699.go
Normal file
624
llmjob/7699.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user