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

169 lines
4.6 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.

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"
)
// AtrRsiStrategy 存储每个品种的持仓期间最高价和最低价
type AtrRsiStrategy struct {
IntraTradeHigh float64
IntraTradeLow float64
}
func J2625(pol *config.RunPolicyConfig) *strat.TradeStrat {
atrLength := pol.DefInt("atr_length", 18, core.PNorm(10, 50))
atrMaRate := pol.Def("atr_ma_rate", 1.8, core.PNorm(1.2, 3.0))
rsiLength := pol.DefInt("rsi_length", 9, core.PNorm(7, 30))
rsiEntry := pol.DefInt("rsi_entry", 7, core.PNorm(5, 20))
atrMaNormMin := pol.Def("atr_ma_norm_min", 0.3, core.PNorm(0.1, 0.5))
atrMaNormMax := pol.Def("atr_ma_norm_max", 0.7, core.PNorm(0.5, 1.0))
trailingPercent := pol.Def("trailing_percent", 1.3, core.PNorm(0.5, 3.0))
// 计算ATR移动平均线周期
atrMaLength := int(float64(atrLength) * atrMaRate)
// 计算RSI买入和卖出阈值
rsiBuy := 50 + float64(rsiEntry)
rsiSell := 50 - float64(rsiEntry)
// 确定预热数量,取最大参数值
warmupNum := atrMaLength
if rsiLength > warmupNum {
warmupNum = rsiLength
}
return &strat.TradeStrat{
WarmupNum: warmupNum,
HedgeOff: true, // 单向持仓模式
RunTimeFrames: []string{"1h"},
OnStartUp: func(s *strat.StratJob) {
// 初始化每个品种的持仓期间最高价和最低价
s.More = &AtrRsiStrategy{
IntraTradeHigh: 0,
IntraTradeLow: 0,
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
m, _ := s.More.(*AtrRsiStrategy)
// 计算技术指标
atrValue := ta.ATR(e.High, e.Low, e.Close, atrLength)
atrMa := ta.SMA(atrValue, atrMaLength)
rsiValue := ta.RSI(e.Close, rsiLength)
// 获取当前价格
closePrice := e.Close.Get(0)
highPrice := e.High.Get(0)
lowPrice := e.Low.Get(0)
// 检查数据有效性
if math.IsNaN(closePrice) || math.IsNaN(highPrice) || math.IsNaN(lowPrice) ||
math.IsNaN(atrValue.Get(0)) || math.IsNaN(atrMa.Get(0)) || math.IsNaN(rsiValue.Get(0)) {
return
}
// 计算ATR移动平均线的标准化值
atrMaNorm := atrMa.Get(0) / closePrice * 100
// 获取当前持仓状态
longOrders := s.LongOrders
shortOrders := s.ShortOrders
hasLongPosition := len(longOrders) > 0
hasShortPosition := len(shortOrders) > 0
// 无持仓状态
if !hasLongPosition && !hasShortPosition {
// 重置持仓期间最高价和最低价
m.IntraTradeHigh = highPrice
m.IntraTradeLow = lowPrice
// 检查波动率是否在适合交易范围内
if atrMaNorm >= atrMaNormMin && atrMaNorm <= atrMaNormMax {
// 检查当前波动率是否高于平均波动率
if atrValue.Get(0) > atrMa.Get(0) {
// 检查RSI信号
if rsiValue.Get(0) > rsiBuy {
// RSI超买,做多
s.OpenOrder(&strat.EnterReq{
Tag: "B1",
OrderType: core.OrderTypeLimit,
Limit: closePrice + 5,
})
} else if rsiValue.Get(0) < rsiSell {
// RSI超卖,做空
s.OpenOrder(&strat.EnterReq{
Tag: "S1",
Short: true,
OrderType: core.OrderTypeLimit,
Limit: closePrice - 5,
})
}
}
}
} else if hasLongPosition {
// 多头持仓状态
// 更新持仓期间最高价
if highPrice > m.IntraTradeHigh {
m.IntraTradeHigh = highPrice
}
m.IntraTradeLow = lowPrice
// 计算多头追踪止损价格
longTp := m.IntraTradeHigh * (1 - trailingPercent/100)
// 获取入场价格
enterPrice := longOrders[0].InitPrice
// 设置止损和止盈
for _, od := range longOrders {
od.SetStopLoss(&ormo.ExitTrigger{
Price: longTp,
Rate: 1.0, // 全部退出
Tag: "trailing_stop",
})
od.SetTakeProfit(&ormo.ExitTrigger{
Price: enterPrice * 1.03,
Rate: 1.0, // 全部退出
Tag: "take_profit",
})
}
} else if hasShortPosition {
// 空头持仓状态
m.IntraTradeHigh = highPrice
// 更新持仓期间最低价
if lowPrice < m.IntraTradeLow {
m.IntraTradeLow = lowPrice
}
// 计算空头追踪止损价格
shortTp := m.IntraTradeLow * (1 + trailingPercent/100)
// 获取入场价格
enterPrice := shortOrders[0].InitPrice
// 设置止损和止盈
for _, od := range shortOrders {
od.SetStopLoss(&ormo.ExitTrigger{
Price: shortTp,
Rate: 1.0, // 全部退出
Tag: "trailing_stop",
})
od.SetTakeProfit(&ormo.ExitTrigger{
Price: enterPrice * 0.94,
Rate: 1.0, // 全部退出
Tag: "take_profit",
})
}
}
},
}
}