250 lines
7.7 KiB
Go
250 lines
7.7 KiB
Go
// 修复后的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"})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
}
|
||
}
|