300 lines
8.5 KiB
Go
300 lines
8.5 KiB
Go
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,
|
||
},
|
||
}
|
||
}
|
||
},
|
||
}
|
||
}
|