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

300 lines
8.5 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/strat"
ta "github.com/banbox/banta"
"math"
)
func J656(pol *config.RunPolicyConfig) *strat.TradeStrat {
shortPeriod := pol.DefInt("shortPeriod", 50, core.PNorm(10, 100))
maRate := pol.Def("maRate", 2.2, core.PNorm(1.5, 4.0))
smoothMethod := pol.DefInt("smoothMethod", 1, core.PUniform(1, 4))
useCurrentRes := pol.DefInt("useCurrentRes", 1, core.PUniform(0, 1))
priceSource := pol.DefInt("priceSource", 0, core.PUniform(0, 7)) // 0=close, 1=high, 2=low, 3=open, 4=hl2, 5=hlc3, 6=hlcc4, 7=ohlc4
// 计算长期均线周期
longPeriod := int(float64(shortPeriod) * maRate)
// 确定预热数量,取长期均线周期的两倍以确保足够数据
warmupNum := longPeriod * 2
return &strat.TradeStrat{
WarmupNum: warmupNum,
HedgeOff: true, // 单向持仓模式
RunTimeFrames: []string{"1h"},
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
// 如果不使用当前时间框架,则需要添加额外的时间框架数据
if useCurrentRes == 0 {
// 使用周线作为额外时间框架,可根据需要调整
return []*strat.PairSub{{Pair: "_cur_", TimeFrame: "W", WarmupNum: warmupNum}}
}
return nil
},
OnBar: func(s *strat.StratJob) {
e := s.Env
// 获取价格数据,根据用户选择的价格源
var price *ta.Series
switch priceSource {
case 0: // close
price = e.Close
case 1: // high
price = e.High
case 2: // low
price = e.Low
case 3: // open
price = e.Open
case 4: // hl2
price = ta.HL2(e.High, e.Low)
case 5: // hlc3
price = ta.HLC3(e.High, e.Low, e.Close)
case 6: // hlcc4
// 使用基本运算实现 hlcc4 = (high + low + close + close) / 4
price = e.High.Add(e.Low).Add(e.Close).Add(e.Close).Div(4)
case 7: // ohlc4
// 使用基本运算实现 ohlc4 = (open + high + low + close) / 4
price = e.Open.Add(e.High).Add(e.Low).Add(e.Close).Div(4)
default:
price = e.Close
}
// 如果不使用当前时间框架,则从More中获取其他时间框架的数据
if useCurrentRes == 0 {
// 从More中获取其他时间框架的价格数据和移动平均线
if s.More == nil {
return
}
moreData, ok := s.More.(map[string]interface{})
if !ok {
return
}
if mtData, ok := moreData["W"]; ok {
if mtPrice, ok := mtData.(*ta.Series); ok {
price = mtPrice
}
if mtMA, ok := mtData.(map[string]interface{})["ma"]; ok {
if maData, ok := mtMA.(map[string]*ta.Series); ok {
if shortMA, ok := maData["short"]; ok {
if longMA, ok := maData["long"]; ok {
// 获取当前和前一个值
shortMA0 := shortMA.Get(0)
shortMA1 := shortMA.Get(1)
longMA0 := longMA.Get(0)
longMA1 := longMA.Get(1)
close0 := e.Close.Get(0)
close1 := e.Close.Get(1)
// 检查数据有效性
if math.IsNaN(shortMA0) || math.IsNaN(shortMA1) || math.IsNaN(longMA0) || math.IsNaN(longMA1) ||
math.IsNaN(close0) || math.IsNaN(close1) {
return
}
// 计算均线交叉信号
crossUp := shortMA1 <= longMA1 && shortMA0 > longMA0
crossDown := shortMA1 >= longMA1 && shortMA0 < longMA0
// 计算价格趋势
priceUp := close0 > close1
priceDown := close0 < close1
// 生成交易信号
buySignal := crossUp && priceUp
sellSignal := crossDown && priceDown
// 执行交易
if buySignal {
// 如果没有多头持仓,则开多单
if len(s.LongOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "Enter Long",
})
}
// 如果有空头持仓,则平空单
if len(s.ShortOrders) > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "Close Short",
Dirt: core.OdDirtShort,
})
}
} else if sellSignal {
// 如果没有空头持仓,则开空单
if len(s.ShortOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "Enter Short",
Short: true,
})
}
// 如果有多头持仓,则平多单
if len(s.LongOrders) > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "Close Long",
Dirt: core.OdDirtLong,
})
}
}
return
}
}
}
}
}
return
}
// 确保有足够的数据
if price.Len() < longPeriod {
return
}
// 根据选择的计算方法计算短期和长期移动平均线
var shortMA, longMA *ta.Series
switch smoothMethod {
case 1: // SMA
shortMA = ta.SMA(price, shortPeriod)
longMA = ta.SMA(price, longPeriod)
case 2: // EMA
shortMA = ta.EMA(price, shortPeriod)
longMA = ta.EMA(price, longPeriod)
case 3: // WMA
shortMA = ta.WMA(price, shortPeriod)
longMA = ta.WMA(price, longPeriod)
case 4: // 线性回归
shortMA = ta.LinReg(price, shortPeriod)
longMA = ta.LinReg(price, longPeriod)
default:
shortMA = ta.EMA(price, shortPeriod)
longMA = ta.EMA(price, longPeriod)
}
// 获取当前和前一个值
shortMA0 := shortMA.Get(0)
shortMA1 := shortMA.Get(1)
longMA0 := longMA.Get(0)
longMA1 := longMA.Get(1)
close0 := e.Close.Get(0)
close1 := e.Close.Get(1)
// 检查数据有效性
if math.IsNaN(shortMA0) || math.IsNaN(shortMA1) || math.IsNaN(longMA0) || math.IsNaN(longMA1) ||
math.IsNaN(close0) || math.IsNaN(close1) {
return
}
// 计算均线交叉信号
crossUp := shortMA1 <= longMA1 && shortMA0 > longMA0
crossDown := shortMA1 >= longMA1 && shortMA0 < longMA0
// 计算价格趋势
priceUp := close0 > close1
priceDown := close0 < close1
// 生成交易信号
buySignal := crossUp && priceUp
sellSignal := crossDown && priceDown
// 执行交易
if buySignal {
// 如果没有多头持仓,则开多单
if len(s.LongOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "Enter Long",
})
}
// 如果有空头持仓,则平空单
if len(s.ShortOrders) > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "Close Short",
Dirt: core.OdDirtShort,
})
}
} else if sellSignal {
// 如果没有空头持仓,则开空单
if len(s.ShortOrders) == 0 {
s.OpenOrder(&strat.EnterReq{
Tag: "Enter Short",
Short: true,
})
}
// 如果有多头持仓,则平多单
if len(s.LongOrders) > 0 {
s.CloseOrders(&strat.ExitReq{
Tag: "Close Long",
Dirt: core.OdDirtLong,
})
}
}
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
// 处理其他时间框架的数据
if useCurrentRes == 0 && tf == "W" {
// 根据用户选择的价格源获取价格数据
var price *ta.Series
switch priceSource {
case 0: // close
price = e.Close
case 1: // high
price = e.High
case 2: // low
price = e.Low
case 3: // open
price = e.Open
case 4: // hl2
price = ta.HL2(e.High, e.Low)
case 5: // hlc3
price = ta.HLC3(e.High, e.Low, e.Close)
case 6: // hlcc4
// 使用基本运算实现 hlcc4 = (high + low + close + close) / 4
price = e.High.Add(e.Low).Add(e.Close).Add(e.Close).Div(4)
case 7: // ohlc4
// 使用基本运算实现 ohlc4 = (open + high + low + close) / 4
price = e.Open.Add(e.High).Add(e.Low).Add(e.Close).Div(4)
default:
price = e.Close
}
// 计算移动平均线
var shortMA, longMA *ta.Series
switch smoothMethod {
case 1: // SMA
shortMA = ta.SMA(price, shortPeriod)
longMA = ta.SMA(price, longPeriod)
case 2: // EMA
shortMA = ta.EMA(price, shortPeriod)
longMA = ta.EMA(price, longPeriod)
case 3: // WMA
shortMA = ta.WMA(price, shortPeriod)
longMA = ta.WMA(price, longPeriod)
case 4: // 线性回归
shortMA = ta.LinReg(price, shortPeriod)
longMA = ta.LinReg(price, longPeriod)
default:
shortMA = ta.EMA(price, shortPeriod)
longMA = ta.EMA(price, longPeriod)
}
// 存储计算结果到s.More中
if s.More == nil {
s.More = make(map[string]interface{})
}
moreData, ok := s.More.(map[string]interface{})
if !ok {
s.More = make(map[string]interface{})
moreData = s.More.(map[string]interface{})
}
moreData["W"] = map[string]interface{}{
"price": price,
"ma": map[string]*ta.Series{
"short": shortMA,
"long": longMA,
},
}
}
},
}
}