Files
banstagy/llmjob/5129.go
2026-08-24 20:53:21 +08:00

250 lines
7.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 修复后的banbot完整策略代码
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"
)
type MfiStrategyState struct {
DayStart int64
DayLow float64
DayHigh float64
EntryTime int64
LongSlPrice float64
ShortSlPrice float64
LongTpPrice float64
ShortTpPrice float64
TrailStop float64
TrailStopShort float64
EntryHigh float64
EntryLow float64
}
func J5129(pol *config.RunPolicyConfig) *strat.TradeStrat {
mfiPeriod := pol.DefInt("mfiPeriod", 3, core.PNorm(2, 10))
barBodySize := pol.Def("barBodySize", 0.65, core.PNorm(0.3, 0.8))
maPeriod := pol.DefInt("maPeriod", 120, core.PNorm(20, 200))
useMaFilter := pol.Def("useMaFilter", 1.0, nil)
useShorts := pol.Def("useShorts", 1.0, nil)
slType := pol.Def("slType", 0, nil) // 0:Strategy Stop, 1:Swing, 2:ATR
swingLookback := pol.DefInt("swingLookback", 10, core.PNorm(5, 30))
swingPercInc := pol.Def("swingPercInc", 0.03, core.PNorm(0.01, 0.1))
atrLength := pol.DefInt("atrLength", 14, core.PNorm(7, 30))
atrMultiple := pol.Def("atrMultiple", 6.5, core.PNorm(2.0, 10.0))
rewardRatio := pol.Def("rewardRatio", 3.2, core.PNorm(1.5, 4.0))
useTpSl := pol.Def("useTpSl", 1.0, nil)
useTimedExit := pol.Def("useTimedExit", 0.0, nil)
useTrailingStop := pol.Def("useTrailingStop", 0.0, nil)
allowDirectPositionReverse := pol.Def("allowDirectPositionReverse", 1.0, nil)
return &strat.TradeStrat{
Name: "Money Flow Index 5 min Strategy",
Version: 1,
WarmupNum: 100,
HedgeOff: false,
EachMaxLong: 1,
EachMaxShort: 1,
RunTimeFrames: []string{"15m"},
OnStartUp: func(s *strat.StratJob) {
s.More = &MfiStrategyState{
DayStart: -1,
DayLow: math.Inf(1),
DayHigh: math.Inf(-1),
EntryTime: 0,
LongSlPrice: math.NaN(),
ShortSlPrice: math.NaN(),
LongTpPrice: math.NaN(),
ShortTpPrice: math.NaN(),
TrailStop: math.NaN(),
TrailStopShort: math.NaN(),
EntryHigh: math.NaN(),
EntryLow: math.NaN(),
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
now := e.TimeStop
c, h, l, o := e.Close.Get(0), e.High.Get(0), e.Low.Get(0), e.Open.Get(0)
if math.IsNaN(c) || math.IsNaN(h) || math.IsNaN(l) {
return
}
// 更新交易日状态
t := time.UnixMilli(now)
dayStartMs := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()).UnixMilli()
m := s.More.(*MfiStrategyState)
if m.DayStart != dayStartMs {
m.DayStart = dayStartMs
m.DayLow = l
m.DayHigh = h
} else {
if l < m.DayLow {
m.DayLow = l
}
if h > m.DayHigh {
m.DayHigh = h
}
}
// 计算指标
mfi := ta.MFI(e, mfiPeriod)
smaVal := ta.SMA(e.Close, maPeriod)
maFilter := c > smaVal.Get(0)
atr := ta.ATR(e.High, e.Low, e.Close, atrLength).Get(0)
// K线实体判断 - 修复错误3:与原始策略保持一致
barSize := h - l
var barBodySizeVal float64
if c > o {
barBodySizeVal = (o - c) * -1
} else {
barBodySizeVal = (o - c)
}
shortWicks := barBodySizeVal > barSize*barBodySize
// 交易信号
prevMfi := mfi.Get(1)
buySignal := !math.IsNaN(prevMfi) && prevMfi == 100 && c > o && shortWicks && (useMaFilter == 0.0 || maFilter)
sellSignal := false
if useShorts == 1.0 {
sellSignal = !math.IsNaN(prevMfi) && prevMfi == 0 && c < o && shortWicks && (useMaFilter == 0.0 || !maFilter)
}
// 检查60分钟退出规则 - 修复错误2:添加60分钟退出规则
if useTimedExit == 1.0 && m.EntryTime > 0 {
timeSinceEntry := (now - m.EntryTime) / 60000 // 转换为分钟
if timeSinceEntry >= 60 {
// 60分钟后平仓
if len(s.LongOrders) > 0 || len(s.ShortOrders) > 0 {
s.CloseOrders(&strat.ExitReq{Dirt: core.OdDirtBoth})
m.EntryTime = 0
}
}
}
// 计算止损止盈 - 修复错误1和4:记录开仓时的止损价格
var longSl, shortSl, longTp, shortTp float64
if useTpSl == 1.0 {
switch int(slType) {
case 0: // Strategy Stop
longSl = m.DayLow * (1 - swingPercInc)
shortSl = m.DayHigh * (1 + swingPercInc)
case 1: // Swing Lo/Hi
ll := ta.Lowest(e.Low, swingLookback).Get(0) * (1 - swingPercInc)
hh := ta.Highest(e.High, swingLookback).Get(0) * (1 + swingPercInc)
longSl = ll
shortSl = hh
case 2: // ATR Stop
ohlc4 := (o + h + l + c) / 4
longSl = ohlc4 - atr*atrMultiple
shortSl = ohlc4 + atr*atrMultiple
}
longTp = c + (c-longSl)*rewardRatio
shortTp = c - (shortSl-c)*rewardRatio
}
// 更新追踪止损 - 修复错误5:添加追踪止损功能
if useTrailingStop == 1.0 {
if len(s.LongOrders) > 0 {
for _, pos := range s.LongOrders {
if pos.Status == ormo.InOutStatusFullEnter { // 多头仓位
if math.IsNaN(m.EntryHigh) || h > m.EntryHigh {
m.EntryHigh = h
}
trailOffset := pos.InitPrice - m.LongSlPrice
newTrailStop := m.EntryHigh - trailOffset
if math.IsNaN(m.TrailStop) || newTrailStop > m.TrailStop {
m.TrailStop = newTrailStop
}
}
}
}
if len(s.ShortOrders) > 0 {
for _, pos := range s.ShortOrders {
if pos.Status == ormo.InOutStatusFullEnter { // 空头仓位
if math.IsNaN(m.EntryLow) || l < m.EntryLow {
m.EntryLow = l
}
trailOffset := m.ShortSlPrice - pos.InitPrice
newTrailStop := m.EntryLow + trailOffset
if math.IsNaN(m.TrailStopShort) || newTrailStop < m.TrailStopShort {
m.TrailStopShort = newTrailStop
}
}
}
}
}
// 开仓 - 修复错误6:检查持仓数量和挂单数量
canOpenLong := len(s.LongOrders) == 0 && (len(s.ShortOrders) == 0 || (allowDirectPositionReverse == 1.0 && len(s.ShortOrders) > 0))
canOpenShort := len(s.ShortOrders) == 0 && (len(s.LongOrders) == 0 || (allowDirectPositionReverse == 1.0 && len(s.LongOrders) > 0))
if buySignal && canOpenLong {
// 记录开仓时的止损价格
m.LongSlPrice = longSl
m.LongTpPrice = longTp
m.EntryHigh = h
m.EntryTime = now
req := &strat.EnterReq{
Tag: "long",
StopLoss: longSl,
TakeProfit: longTp,
StopLossTag: "sl",
TakeProfitTag: "tp",
}
s.OpenOrder(req)
}
if sellSignal && canOpenShort {
// 记录开仓时的止损价格
m.ShortSlPrice = shortSl
m.ShortTpPrice = shortTp
m.EntryLow = l
m.EntryTime = now
req := &strat.EnterReq{
Tag: "short",
Short: true,
StopLoss: shortSl,
TakeProfit: shortTp,
StopLossTag: "sl",
TakeProfitTag: "tp",
}
s.OpenOrder(req)
}
// 更新现有订单的止损止盈
if len(s.LongOrders) > 0 && !math.IsNaN(m.LongSlPrice) {
for _, order := range s.LongOrders {
if order.Status == ormo.InOutStatusInit {
order.SetStopLoss(&ormo.ExitTrigger{Price: m.LongSlPrice, Tag: "sl"})
order.SetTakeProfit(&ormo.ExitTrigger{Price: m.LongTpPrice, Tag: "tp"})
if useTrailingStop == 1.0 && !math.IsNaN(m.TrailStop) {
order.SetStopLoss(&ormo.ExitTrigger{Price: m.TrailStop, Tag: "sl"})
}
}
}
}
if len(s.ShortOrders) > 0 && !math.IsNaN(m.ShortSlPrice) {
for _, order := range s.ShortOrders {
if order.Status == ormo.InOutStatusInit {
order.SetStopLoss(&ormo.ExitTrigger{Price: m.ShortSlPrice, Tag: "sl"})
order.SetTakeProfit(&ormo.ExitTrigger{Price: m.ShortTpPrice, Tag: "tp"})
if useTrailingStop == 1.0 && !math.IsNaN(m.TrailStopShort) {
order.SetStopLoss(&ormo.ExitTrigger{Price: m.TrailStopShort, Tag: "sl"})
}
}
}
}
},
}
}