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