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

477
llmjob/4067.go Normal file
View File

@@ -0,0 +1,477 @@
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"
"math"
"time"
)
// State4067 存储每个品种的策略状态
type State4067 struct {
step int
inTrade bool
tradeDirection int // 1=多头, -1=空头, 0=无
entryPrice float64
tp1Hit bool
buytp1Hit bool
buySLPrice float64
buyTP1Price float64
buyTP2Price float64
selltp1Hit bool
sellSLPrice float64
sellTP1Price float64
sellTP2Price float64
higherEMA float64 // 更高时间周期EMA值
}
// MTFCross 多时间周期EMA交叉策略
func J4067(pol *config.RunPolicyConfig) *strat.TradeStrat {
// 定义可调参数
fastPeriod := pol.DefInt("fastPeriod", 9, core.PNorm(5, 20))
slowRate := pol.Def("slowRate", 5.56, core.PNorm(2, 10))
higherEMARate := pol.Def("higherEMARate", 2, core.PNorm(1.5, 5))
slRate := pol.Def("slRate", 1.6, core.PNorm(0.5, 5))
tp1Rate := pol.Def("tp1Rate", 1.4, core.PNorm(0.5, 3))
beoffRate := pol.Def("beoffRate", 0.3, core.PNorm(0.1, 1))
atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 30))
// 交易时间控制参数
startHour := pol.DefInt("startHour", 1, core.PNorm(0, 23))
endHour := pol.DefInt("endHour", 25, core.PNorm(0, 47))
tradeOnMonday := pol.DefInt("tradeOnMonday", 1, core.PNorm(0, 1)) != 0
tradeOnTuesday := pol.DefInt("tradeOnTuesday", 1, core.PNorm(0, 1)) != 0
tradeOnWednesday := pol.DefInt("tradeOnWednesday", 1, core.PNorm(0, 1)) != 0
tradeOnThursday := pol.DefInt("tradeOnThursday", 1, core.PNorm(0, 1)) != 0
tradeOnFriday := pol.DefInt("tradeOnFriday", 1, core.PNorm(0, 1)) != 0
// 计算衍生参数
slowPeriod := int(float64(fastPeriod) * slowRate)
higherPeriod := int(float64(slowPeriod) * higherEMARate)
// 调整endHour为24小时格式
adjustedEndHour := endHour % 24
return &strat.TradeStrat{
WarmupNum: slowPeriod, // 预热K线数量
HedgeOff: true, // 单向持仓
EachMaxLong: 1, // 最多一个多头订单
EachMaxShort: 1, // 最多一个空头订单
RunTimeFrames: []string{"4h"},
// 初始化策略状态
OnStartUp: func(s *strat.StratJob) {
s.More = &State4067{
step: 0,
inTrade: false,
tradeDirection: 0,
entryPrice: math.NaN(),
tp1Hit: false,
buytp1Hit: false,
selltp1Hit: false,
buySLPrice: math.NaN(),
buyTP1Price: math.NaN(),
buyTP2Price: math.NaN(),
sellSLPrice: math.NaN(),
sellTP1Price: math.NaN(),
sellTP2Price: math.NaN(),
higherEMA: math.NaN(),
}
},
// 定义需要订阅的其他时间周期数据
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
return []*strat.PairSub{
{Pair: "_cur_", TimeFrame: "15m", WarmupNum: higherPeriod},
}
},
// 处理更高时间周期数据
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
state, _ := s.More.(*State4067)
// 计算更高时间周期的EMA
higherEMA := ta.EMA(e.Close, higherPeriod)
if higherEMA.Len() > 0 {
state.higherEMA = higherEMA.Get(0)
}
},
// 主策略逻辑
OnBar: func(s *strat.StratJob) {
e := s.Env
state, _ := s.More.(*State4067)
// 计算技术指标
fastEMA := ta.EMA(e.Close, fastPeriod)
slowEMA := ta.EMA(e.Close, slowPeriod)
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
// 获取当前值
closePrice := e.Close.Get(0)
fastEMAV := fastEMA.Get(0)
slowEMAV := slowEMA.Get(0)
atrV := atr.Get(0)
// 检查数据有效性
if math.IsNaN(closePrice) || math.IsNaN(fastEMAV) || math.IsNaN(slowEMAV) ||
math.IsNaN(atrV) || math.IsNaN(state.higherEMA) {
return
}
// 计算止损止盈距离
slDist := atrV * slRate
tp1Dist := atrV * tp1Rate
tp2Dist := atrV * tp1Rate * 2 // 第二止盈是第一止盈的2倍
beoffDist := atrV * beoffRate
// 检查交易时间
t := time.UnixMilli(e.TimeStart)
isTradingTime := checkTradingTime(t, startHour, adjustedEndHour,
tradeOnMonday, tradeOnTuesday, tradeOnWednesday, tradeOnThursday, tradeOnFriday)
// 更新信号状态步骤
updateStep(state, fastEMA, slowEMA)
// 生成交易信号(仅在交易时间内)
if isTradingTime {
if state.step == 1 && !state.inTrade && fastEMAV > slowEMAV && fastEMAV > state.higherEMA {
// 买入信号
state.inTrade = true
state.tradeDirection = 1
state.entryPrice = closePrice
state.buytp1Hit = false
state.tp1Hit = false
state.buySLPrice = state.entryPrice - slDist
state.buyTP1Price = state.entryPrice + tp1Dist
state.buyTP2Price = state.entryPrice + tp2Dist
state.step = 3
// 开多单
//s.OpenOrder(&strat.EnterReq{
// Tag: "Buy",
// Short: false,
// StopLoss: state.buySLPrice,
// TakeProfit: state.buyTP1Price,
// StopLossVal: slDist,
//})
} else if state.step == 2 && !state.inTrade && fastEMAV < slowEMAV && fastEMAV < state.higherEMA {
// 卖出信号
state.inTrade = true
state.tradeDirection = -1
state.entryPrice = closePrice
state.selltp1Hit = false
state.tp1Hit = false
state.sellSLPrice = state.entryPrice + slDist
state.sellTP1Price = state.entryPrice - tp1Dist
state.sellTP2Price = state.entryPrice - tp2Dist
state.step = 4
// 开空单
s.OpenOrder(&strat.EnterReq{
Tag: "Sell",
Short: true,
StopLoss: state.sellSLPrice,
TakeProfit: state.sellTP1Price,
StopLossVal: slDist,
})
}
}
// 管理多头仓位
if state.inTrade && state.tradeDirection == 1 && len(s.LongOrders) > 0 {
od := s.LongOrders[0]
highPrice := e.High.Get(0)
lowPrice := e.Low.Get(0)
// 检查是否达到第一止盈目标
if highPrice >= state.buyTP1Price && !state.tp1Hit {
state.tp1Hit = true
state.buytp1Hit = true
// 移动止损到保本位置
state.buySLPrice = state.entryPrice + beoffDist
// 平仓25%
s.CloseOrders(&strat.ExitReq{
Tag: "TP1 Hit",
Dirt: core.OdDirtLong,
ExitRate: 0.25,
OrderType: core.OrderTypeMarket,
})
// 更新剩余仓位的止损止盈
od.SetStopLoss(&ormo.ExitTrigger{
Price: state.buySLPrice,
Rate: 1.0,
Tag: "Breakeven",
})
od.SetTakeProfit(&ormo.ExitTrigger{
Price: state.buyTP2Price,
Rate: 1.0,
Tag: "TP2",
})
}
// 检查是否触发止损
if lowPrice <= state.buySLPrice && !state.tp1Hit {
// 止损出场
s.CloseOrders(&strat.ExitReq{
Tag: "SL Hit",
Dirt: core.OdDirtLong,
ExitRate: 1.0,
OrderType: core.OrderTypeMarket,
})
resetState(state)
}
// 检查是否达到第二止盈目标
if state.tp1Hit && highPrice >= state.buyTP2Price {
// 第二止盈出场
s.CloseOrders(&strat.ExitReq{
Tag: "TP2 Hit",
Dirt: core.OdDirtLong,
ExitRate: 1.0,
OrderType: core.OrderTypeMarket,
})
resetState(state)
}
// 检查保本止损是否触发
if state.tp1Hit && lowPrice <= state.buySLPrice {
// 保本止损出场
s.CloseOrders(&strat.ExitReq{
Tag: "Breakeven Hit",
Dirt: core.OdDirtLong,
ExitRate: 1.0,
OrderType: core.OrderTypeMarket,
})
resetState(state)
}
}
// 管理空头仓位
if state.inTrade && state.tradeDirection == -1 && len(s.ShortOrders) > 0 {
od := s.ShortOrders[0]
highPrice := e.High.Get(0)
lowPrice := e.Low.Get(0)
// 检查是否达到第一止盈目标
if lowPrice <= state.sellTP1Price && !state.tp1Hit {
state.tp1Hit = true
state.selltp1Hit = true
// 移动止损到保本位置
state.sellSLPrice = state.entryPrice - beoffDist
// 平仓25%
s.CloseOrders(&strat.ExitReq{
Tag: "TP1 Hit",
Dirt: core.OdDirtShort,
ExitRate: 0.25,
OrderType: core.OrderTypeMarket,
})
// 更新剩余仓位的止损止盈
od.SetStopLoss(&ormo.ExitTrigger{
Price: state.sellSLPrice,
Rate: 1.0,
Tag: "Breakeven",
})
od.SetTakeProfit(&ormo.ExitTrigger{
Price: state.sellTP2Price,
Rate: 1.0,
Tag: "TP2",
})
}
// 检查是否触发止损
if highPrice >= state.sellSLPrice && !state.tp1Hit {
// 止损出场
s.CloseOrders(&strat.ExitReq{
Tag: "SL Hit",
Dirt: core.OdDirtShort,
ExitRate: 1.0,
OrderType: core.OrderTypeMarket,
})
resetState(state)
}
// 检查是否达到第二止盈目标
if state.tp1Hit && lowPrice <= state.sellTP2Price {
// 第二止盈出场
s.CloseOrders(&strat.ExitReq{
Tag: "TP2 Hit",
Dirt: core.OdDirtShort,
ExitRate: 1.0,
OrderType: core.OrderTypeMarket,
})
resetState(state)
}
// 检查保本止损是否触发
if state.tp1Hit && highPrice >= state.sellSLPrice {
// 保本止损出场
s.CloseOrders(&strat.ExitReq{
Tag: "Breakeven Hit",
Dirt: core.OdDirtShort,
ExitRate: 1.0,
OrderType: core.OrderTypeMarket,
})
resetState(state)
}
}
// 更新交易状态
if state.inTrade && state.tradeDirection == 1 && state.step == 2 {
// 多头收到卖出信号
state.inTrade = false
state.tradeDirection = 0
state.step = 2
}
if state.inTrade && state.tradeDirection == -1 && state.step == 1 {
// 空头收到买入信号
state.inTrade = false
state.tradeDirection = 0
state.step = 1
}
},
// 自定义订单退出逻辑
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
state, _ := s.More.(*State4067)
// 多头订单检查
if !od.Short && state.inTrade && state.tradeDirection == 1 {
lowPrice := s.Env.Low.Get(0)
// 检查是否触发止损
if lowPrice <= state.buySLPrice {
return &strat.ExitReq{
Tag: "SL Hit",
Dirt: core.OdDirtLong,
ExitRate: 1.0,
}
}
// 检查是否达到第二止盈目标
if state.tp1Hit {
highPrice := s.Env.High.Get(0)
if highPrice >= state.buyTP2Price {
return &strat.ExitReq{
Tag: "TP2 Hit",
Dirt: core.OdDirtLong,
ExitRate: 1.0,
}
}
}
}
// 空头订单检查
if od.Short && state.inTrade && state.tradeDirection == -1 {
highPrice := s.Env.High.Get(0)
// 检查是否触发止损
if highPrice >= state.sellSLPrice {
return &strat.ExitReq{
Tag: "SL Hit",
Dirt: core.OdDirtShort,
ExitRate: 1.0,
}
}
// 检查是否达到第二止盈目标
if state.tp1Hit {
lowPrice := s.Env.Low.Get(0)
if lowPrice <= state.sellTP2Price {
return &strat.ExitReq{
Tag: "TP2 Hit",
Dirt: core.OdDirtShort,
ExitRate: 1.0,
}
}
}
}
return nil
},
}
}
// checkTradingTime 检查当前时间是否在交易时间内
func checkTradingTime(t time.Time, startHour, adjustedEndHour int,
tradeOnMonday, tradeOnTuesday, tradeOnWednesday, tradeOnThursday, tradeOnFriday bool) bool {
// 获取当前小时和星期几
currentHour := t.Hour()
currentDay := t.Weekday()
// 检查是否在交易小时内
isTradingHour := false
if startHour < adjustedEndHour {
isTradingHour = currentHour >= startHour && currentHour < adjustedEndHour
} else {
isTradingHour = currentHour >= startHour || currentHour < adjustedEndHour
}
// 检查是否在交易日内
isTradingDay := false
switch currentDay {
case time.Monday:
isTradingDay = tradeOnMonday
case time.Tuesday:
isTradingDay = tradeOnTuesday
case time.Wednesday:
isTradingDay = tradeOnWednesday
case time.Thursday:
isTradingDay = tradeOnThursday
case time.Friday:
isTradingDay = tradeOnFriday
}
return isTradingHour && isTradingDay
}
// updateStep 更新信号状态步骤
func updateStep(state *State4067, fastEMA, slowEMA *ta.Series) {
// 检查EMA交叉
crossUp := fastEMA.Cross(slowEMA)
crossDown := slowEMA.Cross(fastEMA)
// 更新状态步骤
if (state.step == 0 || state.step == 4) && crossUp == 1 && fastEMA.Get(0) > state.higherEMA {
state.step = 1
}
if state.step == 3 && crossUp == 1 && fastEMA.Get(0) > state.higherEMA {
state.step = 3
}
if state.step == 2 && crossUp == 1 && fastEMA.Get(0) > state.higherEMA {
state.step = 1
}
if (state.step == 0 || state.step == 3) && crossDown == 1 && fastEMA.Get(0) < state.higherEMA {
state.step = 2
}
if state.step == 4 && crossDown == 1 && fastEMA.Get(0) < state.higherEMA {
state.step = 4
}
if state.step == 1 && crossDown == 1 && fastEMA.Get(0) < state.higherEMA {
state.step = 2
}
}
// resetState 重置策略状态
func resetState(state *State4067) {
state.inTrade = false
state.tradeDirection = 0
state.entryPrice = math.NaN()
state.tp1Hit = false
state.buytp1Hit = false
state.selltp1Hit = false
state.buySLPrice = math.NaN()
state.buyTP1Price = math.NaN()
state.buyTP2Price = math.NaN()
state.sellSLPrice = math.NaN()
state.sellTP1Price = math.NaN()
state.sellTP2Price = math.NaN()
state.step = 0
}