init
This commit is contained in:
189
llmjob/128.go
Normal file
189
llmjob/128.go
Normal file
@@ -0,0 +1,189 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J128(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
longEntryThreshold := pol.Def("long_entry_threshold", 60.0, core.PNorm(50.0, 70.0))
|
||||
thresholdRange := pol.Def("threshold_range", 14.0, core.PNorm(10.0, 25.0))
|
||||
smaPeriod := pol.DefInt("sma_period", 3, core.PNorm(2, 8))
|
||||
atrPeriod := pol.DefInt("atr_period", 14, core.PNorm(7, 21))
|
||||
tp1ATR := pol.Def("tp1_atr", 3.0, core.PNorm(1.5, 5.0))
|
||||
tp2ATR := pol.Def("tp2_atr", 8.0, core.PNorm(5.0, 12.0))
|
||||
tp3ATR := pol.Def("tp3_atr", 14.0, core.PNorm(10.0, 20.0))
|
||||
tp1Percent := pol.Def("tp1_percent", 12.0, core.PNorm(5.0, 25.0))
|
||||
tp2Percent := pol.Def("tp2_percent", 12.0, core.PNorm(5.0, 25.0))
|
||||
tp3Percent := pol.Def("tp3_percent", 12.0, core.PNorm(5.0, 25.0))
|
||||
useTakeProfit := pol.Def("use_take_profit", 0.0, core.PNorm(0.0, 1.0)) // 0=false, 1=true
|
||||
tradingDirection := pol.Def("trading_direction", 2.0, core.PNorm(0.0, 2.0)) // 0=Long, 1=Short, 2=Both
|
||||
|
||||
// 计算衍生参数
|
||||
shortEntryThreshold := longEntryThreshold - thresholdRange
|
||||
longExitThreshold := shortEntryThreshold
|
||||
shortExitThreshold := longEntryThreshold
|
||||
|
||||
// 确定最长预热周期
|
||||
warmupNum := 55 + smaPeriod + 10
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "FATR",
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1,
|
||||
EachMaxShort: 1,
|
||||
RunTimeFrames: []string{"4h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算真实高低点和真实波动范围
|
||||
trueLow := e.Low.Min(e.Close.Back(1))
|
||||
trueHigh := e.High.Max(e.Close.Back(1))
|
||||
trueRange := trueHigh.Sub(trueLow)
|
||||
|
||||
// 计算买入压力(BP)
|
||||
bp := e.Close.Sub(trueLow)
|
||||
|
||||
// 计算不同周期的压力比率
|
||||
calcRatio := func(len int) *ta.Series {
|
||||
sumBP := ta.Sum(bp, len)
|
||||
sumTR := ta.Sum(trueRange, len)
|
||||
return sumBP.Div(sumTR).Mul(100)
|
||||
}
|
||||
|
||||
// 计算Fibonacci数列周期的压力比率
|
||||
ratio8 := calcRatio(8)
|
||||
ratio13 := calcRatio(13)
|
||||
ratio21 := calcRatio(21)
|
||||
ratio34 := calcRatio(34)
|
||||
ratio55 := calcRatio(55)
|
||||
|
||||
// 计算加权平均值
|
||||
weightedAvg := ratio8.Mul(5).Add(
|
||||
ratio13.Mul(4)).Add(
|
||||
ratio21.Mul(3)).Add(
|
||||
ratio34.Mul(2)).Add(
|
||||
ratio55).Div(15)
|
||||
|
||||
// 应用SMA平滑
|
||||
weightedAvgSMA := ta.SMA(weightedAvg, smaPeriod)
|
||||
|
||||
// 计算ATR值
|
||||
atrValue := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
|
||||
// 获取当前值
|
||||
atrVal := atrValue.Get(0)
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 生成交易信号
|
||||
longCondition := weightedAvgSMA.Cross(longEntryThreshold) == 1
|
||||
shortCondition := weightedAvgSMA.Cross(shortEntryThreshold) == -1
|
||||
longExit := weightedAvgSMA.Cross(longExitThreshold) == -1
|
||||
shortExit := weightedAvgSMA.Cross(shortExitThreshold) == 1
|
||||
|
||||
// 执行交易逻辑
|
||||
isLongEnabled := tradingDirection == 0 || tradingDirection == 2
|
||||
isShortEnabled := tradingDirection == 1 || tradingDirection == 2
|
||||
|
||||
// 多头交易逻辑
|
||||
if isLongEnabled {
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
// 开多单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
})
|
||||
|
||||
// 设置止盈订单
|
||||
if useTakeProfit > 0.5 {
|
||||
tpPrice1 := closePrice + tp1ATR*atrVal
|
||||
tpPrice2 := closePrice + tp2ATR*atrVal
|
||||
tpPrice3 := closePrice + tp3ATR*atrVal
|
||||
|
||||
// 设置多个止盈订单
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP1_Long",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: tp1Percent / 100,
|
||||
Limit: tpPrice1,
|
||||
})
|
||||
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP2_Long",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: tp2Percent / 100,
|
||||
Limit: tpPrice2,
|
||||
})
|
||||
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP3_Long",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: tp3Percent / 100,
|
||||
Limit: tpPrice3,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if longExit && len(s.LongOrders) > 0 {
|
||||
// 平多单
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Long_Exit",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 空头交易逻辑
|
||||
if isShortEnabled {
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
// 开空单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
})
|
||||
|
||||
// 设置止盈订单
|
||||
if useTakeProfit > 0.5 {
|
||||
tpPrice1 := closePrice - tp1ATR*atrVal
|
||||
tpPrice2 := closePrice - tp2ATR*atrVal
|
||||
tpPrice3 := closePrice - tp3ATR*atrVal
|
||||
|
||||
// 设置多个止盈订单
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP1_Short",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: tp1Percent / 100,
|
||||
Limit: tpPrice1,
|
||||
})
|
||||
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP2_Short",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: tp2Percent / 100,
|
||||
Limit: tpPrice2,
|
||||
})
|
||||
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP3_Short",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: tp3Percent / 100,
|
||||
Limit: tpPrice3,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if shortExit && len(s.ShortOrders) > 0 {
|
||||
// 平空单
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Short_Exit",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
59
llmjob/1357.go
Normal file
59
llmjob/1357.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J1357(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
fastLength := pol.DefInt("fastLength", 16, core.PNorm(5, 20))
|
||||
slowLength := pol.DefInt("slowLength", 26, core.PNorm(15, 50))
|
||||
signalLength := pol.DefInt("signalLength", 9, core.PNorm(5, 15))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowLength + signalLength,
|
||||
EachMaxLong: 3,
|
||||
EachMaxShort: 3,
|
||||
HedgeOff: true,
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算MACD指标
|
||||
macd, signal := ta.MACD(e.Close, fastLength, slowLength, signalLength)
|
||||
|
||||
// 检测MACD线与信号线的交叉
|
||||
buySignal := macd.Cross(signal)
|
||||
sellSignal := signal.Cross(macd)
|
||||
|
||||
// 买入信号:MACD线上穿信号线
|
||||
if buySignal == 1 {
|
||||
// 开多单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long Trigger",
|
||||
})
|
||||
// 平空单
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Close Short Trigger",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
|
||||
// 卖出信号:MACD线下穿信号线
|
||||
if sellSignal == 1 {
|
||||
// 开空单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short Trigger",
|
||||
Short: true,
|
||||
})
|
||||
// 平多单
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Close Long Trigger",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
525
llmjob/1525.go
Normal file
525
llmjob/1525.go
Normal file
@@ -0,0 +1,525 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/btime"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm/ormo"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banexg/log"
|
||||
ta "github.com/banbox/banta"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
"go.uber.org/zap"
|
||||
"math"
|
||||
)
|
||||
|
||||
/*
|
||||
根据AtrBase基于不同品种权重,限制范围后更好
|
||||
从4h根据行情自动调整加仓阈值和止盈阈值后:
|
||||
之前:夏普0.93,显示回撤35.8%,最大回撤95.4%,最大资金占用3.2W,总收益119.6%
|
||||
之后:夏普1.38,显示回撤5.66%,最大回撤36.2%,最大资金占用1.64W,总收益79.4%
|
||||
|
||||
尝试使用公式0.383*pow(1+target_qty, x)加仓,符合测试的数据,但效果更差
|
||||
*/
|
||||
|
||||
// State1525S 存储每个品种的策略状态
|
||||
type State1525S struct {
|
||||
avgPrice float64 // 平均持仓成本
|
||||
positionQty float64 // 当前持仓数量
|
||||
tpLimit float64
|
||||
score float64 // 下跌行情力度分数,值域[0,6]
|
||||
}
|
||||
|
||||
func J1525(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
smaPeriod := pol.DefInt("sma_period", 18, core.PNorm(5, 50))
|
||||
targetPercRaw := pol.Def("target_perc", -0.14, core.PNorm(-0.3, -0.05))
|
||||
takeProfitRaw := pol.Def("take_profit", 0.09, core.PNorm(0.05, 0.3))
|
||||
targetQty := pol.Def("target_qty", 0.65, core.PNorm(0.2, 1.0))
|
||||
// 加仓次数,超过20后变差;低于19后有段时间不稳定
|
||||
maxAdd := pol.DefInt("max_add", 19, core.PNorm(1, 30))
|
||||
maxOpenPair := pol.DefInt("max_open_pair", 25, core.PNorm(1, 30)) // 最大同时交易品种
|
||||
|
||||
openPairs := make(map[*strat.StratJob]int)
|
||||
pairLock := deadlock.Mutex{}
|
||||
lenAtr := 20
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
|
||||
targetPerc := targetPercRaw
|
||||
takeProfit := takeProfitRaw
|
||||
|
||||
printMS := int64(0)
|
||||
maxPairs := 0
|
||||
// 加仓倍率,基于targetQty: 1, 0.6, 1 1.7 3 4.7 7.7 12.8 21.1 35 57.5 95 157 256 385 464 767
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: max(smaPeriod, baseAtrLen),
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"5m"},
|
||||
BatchInfo: true,
|
||||
OrderOnRotation: "open",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略状态
|
||||
s.More = &State1525S{}
|
||||
},
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
return []*strat.PairSub{
|
||||
{"_cur_", "4h", 20},
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*State1525S)
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 不利行情下调整参数
|
||||
targetPercJob, takeProfitJob := targetPercRaw, takeProfitRaw
|
||||
if state.score >= 5 {
|
||||
targetPercJob = targetPercRaw * 2
|
||||
takeProfitJob = takeProfitRaw * 0.6
|
||||
} else if state.score >= 4 {
|
||||
targetPercJob = targetPercRaw * 1.7
|
||||
takeProfitJob = takeProfitRaw * 0.7
|
||||
} else if state.score >= 3 {
|
||||
targetPercJob = targetPercRaw * 1.3
|
||||
takeProfitJob = takeProfitRaw * 0.8
|
||||
}
|
||||
targetPercCur := (targetPercJob + targetPerc) / 2
|
||||
takeProfitCur := (takeProfitJob + takeProfit) / 2
|
||||
|
||||
// 计算SMA
|
||||
ma := ta.SMA(e.Close, smaPeriod)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
|
||||
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
|
||||
openRate := 0.006 * closePrice / atrBase
|
||||
if openRate < 1 {
|
||||
openRate = math.Sqrt(openRate)
|
||||
}
|
||||
openRate = max(0.7, min(1.5, openRate))
|
||||
|
||||
// 计算当前持仓盈亏比例
|
||||
var pnl float64
|
||||
if state.positionQty > 0 && state.avgPrice > 0 {
|
||||
pnl = (closePrice - state.avgPrice) / state.avgPrice
|
||||
}
|
||||
|
||||
// 计算止盈价格
|
||||
if state.positionQty > 0 && state.avgPrice > 0 {
|
||||
curTakeProfit := state.avgPrice * (1 + takeProfitCur)
|
||||
if curTakeProfit != state.tpLimit {
|
||||
state.tpLimit = curTakeProfit
|
||||
// 设置限价单止盈
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Take Profit",
|
||||
Limit: state.tpLimit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 首次入场条件:价格上穿SMA且无持仓
|
||||
if e.Close.Cross(ma) == 1 && state.positionQty == 0 {
|
||||
pairLock.Lock()
|
||||
_, allow := openPairs[s]
|
||||
if !allow && len(openPairs) < maxOpenPair {
|
||||
openPairs[s] = 1
|
||||
maxPairs = max(maxPairs, len(openPairs))
|
||||
allow = true
|
||||
}
|
||||
pairLock.Unlock()
|
||||
if allow {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
CostRate: openRate,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 加仓条件:亏损达到目标比例且有持仓
|
||||
if state.positionQty > 0 && pnl <= targetPercCur && len(s.LongOrders) < maxAdd {
|
||||
// 计算加仓数量,确保是整数
|
||||
odNum := len(s.LongOrders)
|
||||
addQty := math.Floor(state.positionQty * targetQty)
|
||||
pairLock.Lock()
|
||||
openPairs[s] = odNum
|
||||
pairLock.Unlock()
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: fmt.Sprintf("%v", odNum),
|
||||
Amount: addQty,
|
||||
})
|
||||
if odNum > 5 {
|
||||
printMS = e.TimeStop
|
||||
}
|
||||
}
|
||||
},
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
|
||||
score := float64(0)
|
||||
if ta.RSI(e.Close, 14).Get(0) < 30 {
|
||||
score += 1
|
||||
}
|
||||
roc := ta.ROC(e.Close, 20).Get(0)
|
||||
if roc < -20 {
|
||||
score += 2
|
||||
} else if roc < -10 {
|
||||
score += 1
|
||||
}
|
||||
plusDi, minusDi := ta.PluMinDI(e.High, e.Low, e.Close, 14)
|
||||
adx := ta.ADX(e.High, e.Low, e.Close, 14).Get(0)
|
||||
if minusDi.Get(0) > plusDi.Get(0) {
|
||||
if adx > 40 {
|
||||
score += 2
|
||||
} else if adx > 25 {
|
||||
score += 1
|
||||
}
|
||||
}
|
||||
ma25 := ta.SMA(e.Close, 25).Get(0)
|
||||
ma99 := ta.SMA(e.Close, 99).Get(0)
|
||||
if ma99 > ma25 {
|
||||
score += 1
|
||||
}
|
||||
state, _ := s.More.(*State1525S)
|
||||
state.score = score
|
||||
},
|
||||
OnBatchInfos: func(tf string, jobs map[string]*strat.JobEnv) {
|
||||
jobNum := len(jobs)
|
||||
if jobNum < 3 {
|
||||
return
|
||||
}
|
||||
downNum, downHeavyNum := float64(0), float64(0)
|
||||
timeStop := int64(0)
|
||||
var last *strat.JobEnv
|
||||
for _, j := range jobs {
|
||||
state, _ := j.Job.More.(*State1525S)
|
||||
if state.score >= 5 {
|
||||
downHeavyNum += 1
|
||||
}
|
||||
if state.score >= 3 {
|
||||
downNum += 1
|
||||
}
|
||||
timeStop = j.Env.TimeStop
|
||||
last = j
|
||||
}
|
||||
downRate := downNum / float64(len(jobs))
|
||||
downHeavyRate := downHeavyNum / float64(len(jobs))
|
||||
if printMS > 0 {
|
||||
printMS = 0
|
||||
dateStr := btime.ToDateStr(timeStop, core.DefaultDateFmt)
|
||||
fmt.Printf("%v downRate: %.3f downHeavyRate: %.3f\n", dateStr, downRate*100, downHeavyRate*100)
|
||||
}
|
||||
if downHeavyRate >= 0.8 {
|
||||
// 极端下跌
|
||||
targetPerc = targetPercRaw * 2
|
||||
takeProfit = takeProfitRaw * 0.5
|
||||
} else if downRate >= 0.85 && downHeavyRate >= 0.6 {
|
||||
// 严重下跌
|
||||
targetPerc = targetPercRaw * 1.8
|
||||
takeProfit = takeProfitRaw * 0.5
|
||||
} else if downRate >= 0.7 && downRate <= 0.85 {
|
||||
// 市场中度下跌
|
||||
targetPerc = targetPercRaw * 1.6
|
||||
takeProfit = takeProfitRaw * 0.65
|
||||
} else if downRate >= 0.5 && downRate <= 0.7 {
|
||||
// 轻度下跌
|
||||
targetPerc = targetPercRaw * 1.3
|
||||
takeProfit = takeProfitRaw * 0.8
|
||||
} else {
|
||||
// 轻度或未下跌
|
||||
targetPerc = targetPercRaw
|
||||
takeProfit = takeProfitRaw
|
||||
}
|
||||
pairLock.Lock()
|
||||
totNum := 0
|
||||
for _, num := range openPairs {
|
||||
totNum += num
|
||||
}
|
||||
pairLock.Unlock()
|
||||
if totNum > 0 {
|
||||
last.Job.Strat.WriteOutput(fmt.Sprintf("pairNum: %v", totNum), true)
|
||||
}
|
||||
},
|
||||
OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) {
|
||||
state, _ := s.More.(*State1525S)
|
||||
|
||||
// 当订单入场时更新持仓状态
|
||||
if chgType == strat.OdChgEnterFill && od.EnterTag != "" {
|
||||
// 计算新的平均成本
|
||||
if state.positionQty == 0 {
|
||||
// 首次开仓
|
||||
state.avgPrice = od.InitPrice
|
||||
state.positionQty = od.HoldAmount()
|
||||
} else {
|
||||
// 加仓,更新平均成本
|
||||
oldQty := state.positionQty
|
||||
oldAvgPrice := state.avgPrice
|
||||
newQty := od.HoldAmount()
|
||||
newAvgPrice := od.InitPrice
|
||||
|
||||
// 计算新的平均成本
|
||||
totalQty := oldQty + newQty
|
||||
totalCost := oldQty*oldAvgPrice + newQty*newAvgPrice
|
||||
state.avgPrice = totalCost / totalQty
|
||||
state.positionQty = totalQty
|
||||
}
|
||||
}
|
||||
|
||||
// 当订单完全平仓时重置持仓状态
|
||||
if chgType == strat.OdChgExitFill && od.ExitTag != "" && od.Status == ormo.InOutStatusFullExit {
|
||||
state.avgPrice = 0
|
||||
state.positionQty = 0
|
||||
state.tpLimit = 0
|
||||
pairLock.Lock()
|
||||
delete(openPairs, s)
|
||||
pairLock.Unlock()
|
||||
}
|
||||
},
|
||||
OnShutDown: func(s *strat.StratJob) {
|
||||
if maxPairs == 0 {
|
||||
return
|
||||
}
|
||||
log.Info("max open pairs", zap.Int("num", maxPairs))
|
||||
maxPairs = 0
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// State1525Sim 存储每个品种的策略状态
|
||||
type State1525Sim struct {
|
||||
totAmount float64
|
||||
totCost float64
|
||||
tpLimit float64 // 止盈出场价格
|
||||
odNum int
|
||||
upFlag float64 // 1: ma20 > ma99 && ma20 > ma20[2]
|
||||
score float64 // 下跌行情力度分数,值域[0,6]
|
||||
}
|
||||
|
||||
func J1525Sim(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 最多30个品种,单笔30,初始3W
|
||||
// 定义可调超参数
|
||||
smaPeriod := pol.DefInt("sma_period", 18, core.PNorm(5, 50))
|
||||
targetPercRaw := pol.Def("target_perc", -0.14, core.PNorm(-0.3, -0.05))
|
||||
takeProfitRaw := pol.Def("take_profit", 0.09, core.PNorm(0.05, 0.3))
|
||||
targetQty := pol.Def("target_qty", 0.6, core.PNorm(0.2, 1.0))
|
||||
// 加仓次数,超过20后变差;低于19后有段时间不稳定
|
||||
maxAdd := pol.DefInt("max_add", 19, core.PNorm(1, 30))
|
||||
startLvlRaw := pol.DefInt("start_lvl", 3, core.PNorm(1, 15))
|
||||
maxOpenPair := pol.DefInt("max_open_pair", 20, core.PNorm(1, 30)) // 最大同时交易品种
|
||||
|
||||
openPairs := make(map[*strat.StratJob]int)
|
||||
pairLock := deadlock.Mutex{}
|
||||
lenAtr := 20
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
|
||||
targetPerc := targetPercRaw
|
||||
takeProfit := takeProfitRaw
|
||||
startLvl := startLvlRaw
|
||||
|
||||
// 加仓倍率,基于targetQty: 1, 0.6, 1 1.7 3 4.7 7.7 12.8 21.1 35 57.5 95 157 256 385 464 767
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: max(smaPeriod, baseAtrLen),
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"5m"},
|
||||
BatchInfo: true,
|
||||
StakeRate: 0.25,
|
||||
OrderOnRotation: "open", // 品种切换时允许开单
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &State1525Sim{}
|
||||
},
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
return []*strat.PairSub{
|
||||
{"_cur_", "4h", 20},
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*State1525Sim)
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 不利行情下调整参数
|
||||
targetPercJob, takeProfitJob := targetPercRaw, takeProfitRaw
|
||||
if state.score >= 5 {
|
||||
targetPercJob = targetPercRaw * 2
|
||||
takeProfitJob = takeProfitRaw * 0.6
|
||||
} else if state.score >= 4 {
|
||||
targetPercJob = targetPercRaw * 1.7
|
||||
takeProfitJob = takeProfitRaw * 0.7
|
||||
} else if state.score >= 3 {
|
||||
targetPercJob = targetPercRaw * 1.3
|
||||
takeProfitJob = takeProfitRaw * 0.8
|
||||
}
|
||||
targetPercCur := (targetPercJob + targetPerc) / 2
|
||||
takeProfitCur := (takeProfitJob + takeProfit) / 2
|
||||
|
||||
// 计算SMA
|
||||
ma := ta.SMA(e.Close, smaPeriod)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
|
||||
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
|
||||
openRate := 0.006 * closePrice / atrBase
|
||||
if openRate < 1 {
|
||||
openRate = math.Sqrt(openRate)
|
||||
}
|
||||
openRate = max(0.7, min(1.5, openRate))
|
||||
|
||||
// 检查止盈触发
|
||||
if state.tpLimit > 0 && state.tpLimit <= e.High.Get(0) {
|
||||
state.totAmount = 0
|
||||
state.totCost = 0
|
||||
state.tpLimit = 0
|
||||
state.odNum = 0
|
||||
pairLock.Lock()
|
||||
delete(openPairs, s)
|
||||
pairLock.Unlock()
|
||||
}
|
||||
|
||||
// 计算当前持仓盈亏比例、止盈
|
||||
var pnl, avgPrice float64
|
||||
if state.totAmount > 0 {
|
||||
avgPrice = state.totCost / state.totAmount
|
||||
pnl = closePrice/avgPrice - 1
|
||||
curTP := avgPrice * (1 + takeProfitCur)
|
||||
if curTP != state.tpLimit {
|
||||
state.tpLimit = curTP
|
||||
// 设置限价单止盈
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Take Profit",
|
||||
Limit: curTP,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 首次入场条件:价格上穿SMA且无持仓
|
||||
if e.Close.Cross(ma) == 1 && state.totAmount == 0 && openRate > 0 {
|
||||
state.odNum = 1
|
||||
state.totAmount = openRate
|
||||
state.totCost = closePrice * openRate
|
||||
}
|
||||
|
||||
// 加仓条件:亏损达到目标比例且有持仓
|
||||
if avgPrice > 0 && pnl <= targetPercCur && state.odNum < maxAdd {
|
||||
amt := state.totAmount * targetQty
|
||||
state.totAmount += amt
|
||||
state.totCost += amt * closePrice
|
||||
curStart := startLvl
|
||||
if state.upFlag < 0 {
|
||||
// 下降趋势上升3个阶梯,和全局阈值取平均
|
||||
curStart = (startLvl + startLvlRaw + 3) / 2
|
||||
}
|
||||
if state.odNum >= curStart {
|
||||
pairLock.Lock()
|
||||
_, allow := openPairs[s]
|
||||
if !allow && len(openPairs) < maxOpenPair {
|
||||
allow = true
|
||||
}
|
||||
if allow {
|
||||
openPairs[s] = len(s.LongOrders)
|
||||
}
|
||||
pairLock.Unlock()
|
||||
if allow {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: fmt.Sprintf("%v", state.odNum),
|
||||
CostRate: amt,
|
||||
})
|
||||
}
|
||||
}
|
||||
state.odNum += 1
|
||||
}
|
||||
},
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
|
||||
score := float64(0)
|
||||
if ta.RSI(e.Close, 14).Get(0) < 30 {
|
||||
score += 1
|
||||
}
|
||||
roc := ta.ROC(e.Close, 20).Get(0)
|
||||
if roc < -20 {
|
||||
score += 2
|
||||
} else if roc < -10 {
|
||||
score += 1
|
||||
}
|
||||
plusDi, minusDi := ta.PluMinDI(e.High, e.Low, e.Close, 14)
|
||||
adx := ta.ADX(e.High, e.Low, e.Close, 14).Get(0)
|
||||
if minusDi.Get(0) > plusDi.Get(0) {
|
||||
if adx > 40 {
|
||||
score += 2
|
||||
} else if adx > 25 {
|
||||
score += 1
|
||||
}
|
||||
}
|
||||
ma7 := ta.SMA(e.Close, 7).Get(0)
|
||||
ma20 := ta.SMA(e.Close, 20)
|
||||
ma20V := ma20.Get(0)
|
||||
ma99 := ta.SMA(e.Close, 99).Get(0)
|
||||
if ma99 > ma20V {
|
||||
score += 1
|
||||
}
|
||||
state, _ := s.More.(*State1525Sim)
|
||||
state.score = score
|
||||
state.upFlag = 0
|
||||
if ma20V > ma99 && ma20V > ma20.Get(2) {
|
||||
state.upFlag = 1
|
||||
} else if ma7 < min(ma20V, ma99) {
|
||||
state.upFlag = -1
|
||||
}
|
||||
},
|
||||
OnBatchInfos: func(tf string, jobs map[string]*strat.JobEnv) {
|
||||
jobNum := len(jobs)
|
||||
if jobNum < 3 {
|
||||
return
|
||||
}
|
||||
downNum, downHeavyNum, sumUp := float64(0), float64(0), float64(0)
|
||||
var last *strat.JobEnv
|
||||
for _, j := range jobs {
|
||||
state, _ := j.Job.More.(*State1525Sim)
|
||||
if state.score >= 5 {
|
||||
downHeavyNum += 1
|
||||
}
|
||||
if state.score >= 3 {
|
||||
downNum += 1
|
||||
}
|
||||
sumUp += max(0, state.upFlag)
|
||||
last = j
|
||||
}
|
||||
downRate := downNum / float64(len(jobs))
|
||||
downHeavyRate := downHeavyNum / float64(len(jobs))
|
||||
sumUp /= float64(len(jobs))
|
||||
if downHeavyRate >= 0.8 {
|
||||
// 极端下跌
|
||||
targetPerc = targetPercRaw * 2
|
||||
takeProfit = takeProfitRaw * 0.5
|
||||
} else if downRate >= 0.85 && downHeavyRate >= 0.6 {
|
||||
// 严重下跌
|
||||
targetPerc = targetPercRaw * 1.8
|
||||
takeProfit = takeProfitRaw * 0.5
|
||||
} else if downRate >= 0.7 && downRate <= 0.85 {
|
||||
// 市场中度下跌
|
||||
targetPerc = targetPercRaw * 1.6
|
||||
takeProfit = takeProfitRaw * 0.65
|
||||
} else if downRate >= 0.5 && downRate <= 0.7 {
|
||||
// 轻度下跌
|
||||
targetPerc = targetPercRaw * 1.3
|
||||
takeProfit = takeProfitRaw * 0.8
|
||||
} else {
|
||||
// 轻度或未下跌
|
||||
targetPerc = targetPercRaw
|
||||
takeProfit = takeProfitRaw
|
||||
}
|
||||
// 调整最小开仓级别
|
||||
if sumUp > 0.7 {
|
||||
startLvl = startLvlRaw
|
||||
} else if sumUp > 0.5 && downRate <= 0.5 {
|
||||
startLvl = startLvlRaw + 2
|
||||
} else if sumUp > 0.3 && downRate <= 0.7 {
|
||||
startLvl = startLvlRaw + 4
|
||||
} else {
|
||||
startLvl = startLvlRaw + 6
|
||||
}
|
||||
pairLock.Lock()
|
||||
totNum := 0
|
||||
for _, num := range openPairs {
|
||||
totNum += num
|
||||
}
|
||||
pairLock.Unlock()
|
||||
if totNum > 0 {
|
||||
last.Job.Strat.WriteOutput(fmt.Sprintf("pairNum: %v", totNum), true)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
105
llmjob/1569.go
Normal file
105
llmjob/1569.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J1569(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastPeriod := pol.DefInt("fastPeriod", 10, core.PNorm(5, 20))
|
||||
smaRatio := pol.Def("smaRatio", 2.0, core.PNorm(1.5, 4.0))
|
||||
supportResistancePeriod := pol.DefInt("supportResistancePeriod", 12, core.PNorm(5, 20))
|
||||
confirmPeriod := pol.DefInt("confirmPeriod", 3, core.PNorm(2, 5))
|
||||
riskRewardRatio := pol.Def("riskRewardRatio", 2.2, core.PNorm(1.5, 3.0))
|
||||
|
||||
// 计算慢速SMA周期
|
||||
slowPeriod := int(float64(fastPeriod) * smaRatio)
|
||||
|
||||
// 确保预热K线数量足够计算所有指标
|
||||
warmupNum := slowPeriod
|
||||
if supportResistancePeriod > warmupNum {
|
||||
warmupNum = supportResistancePeriod
|
||||
}
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多1个多头订单
|
||||
EachMaxShort: 1, // 最多1个空头订单
|
||||
RunTimeFrames: []string{"1h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算移动平均线
|
||||
smaFast := ta.SMA(e.Close, fastPeriod)
|
||||
smaSlow := ta.SMA(e.Close, slowPeriod)
|
||||
|
||||
// 计算支撑阻力位(过去10根K线的最高价和最低价)
|
||||
recentLow := ta.Lowest(e.Low, supportResistancePeriod)
|
||||
recentHigh := ta.Highest(e.High, supportResistancePeriod)
|
||||
|
||||
// 计算确认条件所需的指标
|
||||
highestSmaSlow := ta.Highest(smaSlow, confirmPeriod)
|
||||
lowestSmaSlow := ta.Lowest(smaSlow, confirmPeriod)
|
||||
lowestLow3 := ta.Lowest(e.Low, confirmPeriod)
|
||||
highestHigh3 := ta.Highest(e.High, confirmPeriod)
|
||||
|
||||
// 获取当前值
|
||||
closePrice := e.Close.Get(0)
|
||||
smaSlowVal := smaSlow.Get(0)
|
||||
recentLowVal := recentLow.Get(0)
|
||||
recentHighVal := recentHigh.Get(0)
|
||||
highestSmaSlowVal := highestSmaSlow.Get(0)
|
||||
lowestSmaSlowVal := lowestSmaSlow.Get(0)
|
||||
lowestLow3Val := lowestLow3.Get(0)
|
||||
highestHigh3Val := highestHigh3.Get(0)
|
||||
|
||||
// 生成交易信号 - 只计算一次交叉信号
|
||||
crossSignal := smaFast.Cross(smaSlow)
|
||||
bullishCross := crossSignal == 1 // 10周期SMA上穿20周期SMA
|
||||
bearishCross := crossSignal == -1 // 10周期SMA下穿20周期SMA
|
||||
breakoutUp := closePrice > highestSmaSlowVal // 收盘价突破过去3根K线的20周期SMA最高点
|
||||
breakoutDown := closePrice < lowestSmaSlowVal // 收盘价跌破过去3根K线的20周期SMA最低点
|
||||
retestUp := lowestLow3Val > smaSlowVal // 过去3根K线的最低价保持在20周期SMA之上
|
||||
retestDown := highestHigh3Val < smaSlowVal // 过去3根K线的最高价保持在20周期SMA之下
|
||||
|
||||
// 三重确认条件
|
||||
buyCondition := bullishCross && breakoutUp && retestUp
|
||||
sellCondition := bearishCross && breakoutDown && retestDown
|
||||
|
||||
// 计算止损止盈
|
||||
longSL := recentLowVal // 买入止损设为过去10根K线的最低价
|
||||
shortSL := recentHighVal // 卖出止损设为过去10根K线的最高价
|
||||
|
||||
riskSizeLong := closePrice - longSL // 买入交易的风险大小
|
||||
riskSizeShort := shortSL - closePrice // 卖出交易的风险大小
|
||||
|
||||
longTP := closePrice + (riskSizeLong * riskRewardRatio) // 买入交易的止盈价格
|
||||
shortTP := closePrice - (riskSizeShort * riskRewardRatio) // 卖出交易的止盈价格
|
||||
|
||||
// 执行交易
|
||||
if buyCondition && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLoss: longSL,
|
||||
TakeProfit: longTP,
|
||||
})
|
||||
}
|
||||
|
||||
if sellCondition && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLoss: shortSL,
|
||||
TakeProfit: shortTP,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
197
llmjob/1662.go
Normal file
197
llmjob/1662.go
Normal file
@@ -0,0 +1,197 @@
|
||||
// 修复后的完整策略代码
|
||||
// 请勿修改package包名和任意函数名
|
||||
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"
|
||||
)
|
||||
|
||||
/*
|
||||
因止盈止损触发价未检查是否合理的bug导致虚假净值曲线
|
||||
*/
|
||||
|
||||
// SuperTrendData 存储每个品种的Supertrend相关数据
|
||||
type SuperTrendData struct {
|
||||
trend int // 当前趋势方向,1表示上升趋势,-1表示下降趋势
|
||||
longTrailStop float64 // 多头仓位的移动止损价格
|
||||
shortTrailStop float64 // 空头仓位的移动止损价格
|
||||
up float64 // 上轨值
|
||||
dn float64 // 下轨值
|
||||
}
|
||||
|
||||
func J1662(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
atrPeriod := pol.DefInt("atrPeriod", 8, core.PNorm(5, 30))
|
||||
atrMultiplier := pol.Def("atrMultiplier", 2.5, core.PNorm(1.5, 5.0))
|
||||
|
||||
lenAtr := 20
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: baseAtrLen, // 预热K线数量,确保有足够数据计算指标
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的Supertrend数据
|
||||
s.More = &SuperTrendData{
|
||||
trend: 1, // 初始趋势设为上升
|
||||
longTrailStop: math.NaN(),
|
||||
shortTrailStop: math.NaN(),
|
||||
up: math.NaN(),
|
||||
dn: math.NaN(),
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
data, _ := s.More.(*SuperTrendData)
|
||||
|
||||
// 计算数据源 (hl2 = (high + low) / 2)
|
||||
src := ta.HL2(e.High, e.Low)
|
||||
|
||||
// 计算ATR值
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
|
||||
// 计算Supertrend指标
|
||||
upVal := src.Get(0) - atr.Get(0)*atrMultiplier
|
||||
dnVal := src.Get(0) + atr.Get(0)*atrMultiplier
|
||||
|
||||
atr2 := ta.ATR(e.High, e.Low, e.Close, lenAtr)
|
||||
atrBase := ta.Lowest(ta.Highest(atr2, lenAtr), baseAtrLen).Get(0)
|
||||
|
||||
// 获取历史值
|
||||
up1 := data.up
|
||||
if math.IsNaN(up1) {
|
||||
up1 = upVal
|
||||
}
|
||||
dn1 := data.dn
|
||||
if math.IsNaN(dn1) {
|
||||
dn1 = dnVal
|
||||
}
|
||||
|
||||
// 更新上轨和下轨
|
||||
close1 := e.Close.Get(1)
|
||||
if !math.IsNaN(close1) {
|
||||
if close1 > up1 {
|
||||
upVal = math.Max(upVal, up1)
|
||||
}
|
||||
if close1 < dn1 {
|
||||
dnVal = math.Min(dnVal, dn1)
|
||||
}
|
||||
}
|
||||
|
||||
// 保存当前上下轨值
|
||||
data.up = upVal
|
||||
data.dn = dnVal
|
||||
|
||||
// 判断趋势方向
|
||||
prevTrend := data.trend
|
||||
closePrice := e.Close.Get(0)
|
||||
if !math.IsNaN(closePrice) && !math.IsNaN(dn1) && !math.IsNaN(up1) && !math.IsNaN(atrBase) {
|
||||
if data.trend == -1 && closePrice > dn1 {
|
||||
data.trend = 1 // 趋势由下降转为上升
|
||||
} else if data.trend == 1 && closePrice < up1 {
|
||||
data.trend = -1 // 趋势由上升转为下降
|
||||
}
|
||||
}
|
||||
|
||||
// 生成交易信号
|
||||
buySignal := data.trend == 1 && prevTrend == -1
|
||||
sellSignal := data.trend == -1 && prevTrend == 1
|
||||
|
||||
// 执行交易策略
|
||||
if buySignal {
|
||||
// 开仓多头,使用下轨作为触发价格
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Stop: dnVal,
|
||||
Log: true,
|
||||
})
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "XShort",
|
||||
Dirt: core.OdDirtShort,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
|
||||
if sellSignal {
|
||||
// 开仓空头,使用上轨作为触发价格
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
Stop: upVal,
|
||||
Log: true,
|
||||
})
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "XLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
|
||||
pipValue := atrBase * 0
|
||||
trailingValue := atrBase * 0
|
||||
|
||||
// 移动止损逻辑
|
||||
if s.OrderNum > 0 {
|
||||
// 多头移动止损
|
||||
if len(s.LongOrders) > 0 {
|
||||
// 获取持仓均价
|
||||
avgPrice := s.PositionAvgPrice(core.OdDirtLong, "")
|
||||
|
||||
if avgPrice > 0 {
|
||||
if math.IsNaN(data.longTrailStop) || closePrice > avgPrice+trailingValue {
|
||||
data.longTrailStop = e.High.Get(0) - trailingValue
|
||||
}
|
||||
|
||||
// 计算初始止损
|
||||
longStopLevel := upVal - pipValue
|
||||
// 使用更严格的止损(更接近当前价格的止损)
|
||||
finalStop := math.Max(data.longTrailStop, longStopLevel)
|
||||
if finalStop >= closePrice {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "SLLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
} else {
|
||||
s.SetAllStopLoss(core.OdDirtLong, &ormo.ExitTrigger{
|
||||
Price: finalStop,
|
||||
Tag: "sl",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 空头移动止损
|
||||
if len(s.ShortOrders) > 0 {
|
||||
// 获取持仓均价
|
||||
avgPrice := s.PositionAvgPrice(core.OdDirtShort, "")
|
||||
|
||||
if avgPrice > 0 {
|
||||
if math.IsNaN(data.shortTrailStop) || closePrice < avgPrice-trailingValue {
|
||||
data.shortTrailStop = e.Low.Get(0) + trailingValue
|
||||
}
|
||||
|
||||
// 计算初始止损
|
||||
shortStopLevel := dnVal + pipValue
|
||||
// 使用更严格的止损(更接近当前价格的止损)
|
||||
finalStop := math.Min(data.shortTrailStop, shortStopLevel)
|
||||
if finalStop < closePrice {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "SLShort",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
} else {
|
||||
s.SetAllStopLoss(core.OdDirtShort, &ormo.ExitTrigger{
|
||||
Price: finalStop,
|
||||
Tag: "sl",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
278
llmjob/1665.go
Normal file
278
llmjob/1665.go
Normal file
@@ -0,0 +1,278 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// StrategyState 保存每个品种的交易状态
|
||||
type StrategyState struct {
|
||||
isLong bool // 当前是否持有多头仓位
|
||||
isShort bool // 当前是否持有空头仓位
|
||||
partialTpTriggered bool // 第一止盈是否已触发
|
||||
stopPrice float64 // 止损价格
|
||||
firstTpPrice float64 // 第一止盈价格
|
||||
secondTpPrice float64 // 第二止盈价格
|
||||
firstTpQty float64 // 第一止盈数量
|
||||
positionSize float64 // 仓位大小
|
||||
entryPrice float64 // 入场价格
|
||||
}
|
||||
|
||||
func J1665(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastLength := pol.DefInt("fastLength", 5, core.PNorm(3, 15))
|
||||
emaRate := pol.Def("emaRate", 3.0, core.PNorm(1.5, 6.0))
|
||||
rsiLength := pol.DefInt("rsiLength", 7, core.PNorm(3, 21))
|
||||
atrPeriod := pol.DefInt("atrPeriod", 10, core.PNorm(5, 20))
|
||||
stopLossRate := pol.Def("stopLossRate", 5.0, core.PNorm(2.0, 10.0))
|
||||
firstTakeProfitRate := pol.Def("firstTakeProfitRate", 8.0, core.PNorm(4.0, 16.0))
|
||||
secondTakeProfitRate := pol.Def("secondTakeProfitRate", 12.0, core.PNorm(6.0, 24.0))
|
||||
firstTakeProfitQtyRate := pol.Def("firstTakeProfitQtyRate", 50.0, core.PNorm(25.0, 75.0))
|
||||
leverageRate := pol.Def("leverageRate", 3.0, core.PNorm(1.0, 5.0))
|
||||
|
||||
// 计算慢速EMA长度
|
||||
slowLength := int(float64(fastLength) * emaRate)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowLength + 26, // 确保有足够数据计算所有指标
|
||||
HedgeOff: true, // 单向持仓
|
||||
EachMaxLong: 1, // 最多一个多单
|
||||
EachMaxShort: 1, // 最多一个空单
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略状态
|
||||
s.More = &StrategyState{
|
||||
isLong: false,
|
||||
isShort: false,
|
||||
partialTpTriggered: false,
|
||||
stopPrice: math.NaN(),
|
||||
firstTpPrice: math.NaN(),
|
||||
secondTpPrice: math.NaN(),
|
||||
firstTpQty: math.NaN(),
|
||||
positionSize: math.NaN(),
|
||||
entryPrice: math.NaN(),
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*StrategyState)
|
||||
|
||||
// 计算技术指标
|
||||
fastEMA := ta.EMA(e.Close, fastLength)
|
||||
slowEMA := ta.EMA(e.Close, slowLength)
|
||||
rsi := ta.RSI(e.Close, rsiLength)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
macd, signal := ta.MACD(e.Close, 12, 26, 9)
|
||||
|
||||
// 获取当前值
|
||||
closePrice := e.Close.Get(0)
|
||||
prevClosePrice := e.Close.Get(1)
|
||||
highPrice := e.High.Get(0)
|
||||
lowPrice := e.Low.Get(0)
|
||||
if math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测MACD交叉
|
||||
macdCross := macd.Cross(signal)
|
||||
macdGolden := macdCross == 1 // MACD金叉
|
||||
macdDeath := macdCross == -1 // MACD死叉
|
||||
|
||||
// 生成交易信号
|
||||
longCondition := fastEMA.Get(0) > slowEMA.Get(0) && macdGolden && rsi.Get(0) < 70
|
||||
shortCondition := fastEMA.Get(0) < slowEMA.Get(0) && macdDeath && rsi.Get(0) > 30
|
||||
|
||||
// 平仓信号
|
||||
exitLong := shortCondition || fastEMA.Get(0) < slowEMA.Get(0)
|
||||
exitShort := longCondition || fastEMA.Get(0) > slowEMA.Get(0)
|
||||
|
||||
// 计算仓位大小
|
||||
if !math.IsNaN(atr.Get(0)) && atr.Get(0) > 0 {
|
||||
maxRiskPerTrade := 0.05
|
||||
// 修复错误1:使用动态账户权益而非固定值1000
|
||||
stakeAmount := s.Strat.GetStakeAmount(s)
|
||||
basePosition := stakeAmount * maxRiskPerTrade
|
||||
atrAmount := atr.Get(0) * closePrice
|
||||
riskPosition := basePosition / atrAmount * leverageRate
|
||||
maxPosition := stakeAmount * 0.4 / closePrice
|
||||
state.positionSize = math.Min(riskPosition, maxPosition)
|
||||
// 修复错误6:将firstTpQty的初始化移到仓位计算函数内部
|
||||
state.firstTpQty = state.positionSize * (firstTakeProfitQtyRate / 100.0)
|
||||
}
|
||||
|
||||
// 多头入场
|
||||
if longCondition && !state.isLong && !state.isShort && !math.IsNaN(state.positionSize) {
|
||||
state.entryPrice = closePrice
|
||||
state.stopPrice = closePrice * (1 - stopLossRate/100)
|
||||
state.firstTpPrice = closePrice * (1 + firstTakeProfitRate/100)
|
||||
state.secondTpPrice = closePrice * (1 + secondTakeProfitRate/100)
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
Amount: state.positionSize,
|
||||
StopLoss: state.stopPrice,
|
||||
StopLossTag: "stop_loss",
|
||||
})
|
||||
state.isLong = true
|
||||
state.partialTpTriggered = false
|
||||
}
|
||||
|
||||
// 空头入场
|
||||
if shortCondition && !state.isShort && !state.isLong && !math.IsNaN(state.positionSize) {
|
||||
state.entryPrice = closePrice
|
||||
state.stopPrice = closePrice * (1 + stopLossRate/100)
|
||||
state.firstTpPrice = closePrice * (1 - firstTakeProfitRate/100)
|
||||
state.secondTpPrice = closePrice * (1 - secondTakeProfitRate/100)
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
Short: true,
|
||||
Amount: state.positionSize,
|
||||
StopLoss: state.stopPrice,
|
||||
StopLossTag: "stop_loss",
|
||||
})
|
||||
state.isShort = true
|
||||
state.partialTpTriggered = false
|
||||
}
|
||||
|
||||
// 修复错误2:在每个K线更新时重新计算止盈止损价格
|
||||
if state.isLong {
|
||||
state.stopPrice = state.entryPrice * (1 - stopLossRate/100)
|
||||
state.firstTpPrice = state.entryPrice * (1 + firstTakeProfitRate/100)
|
||||
if !state.partialTpTriggered {
|
||||
state.secondTpPrice = state.entryPrice * (1 + secondTakeProfitRate/100)
|
||||
}
|
||||
|
||||
// 修复错误3:在触发第一止盈时立即调整第二止盈价格
|
||||
if !state.partialTpTriggered && (!math.IsNaN(prevClosePrice) && prevClosePrice >= state.firstTpPrice || highPrice >= state.firstTpPrice) {
|
||||
state.partialTpTriggered = true
|
||||
state.secondTpPrice = highPrice * (1 + 0.04) // 基于当前最高价再上涨4%
|
||||
}
|
||||
}
|
||||
|
||||
if state.isShort {
|
||||
state.stopPrice = state.entryPrice * (1 + stopLossRate/100)
|
||||
state.firstTpPrice = state.entryPrice * (1 - firstTakeProfitRate/100)
|
||||
if !state.partialTpTriggered {
|
||||
state.secondTpPrice = state.entryPrice * (1 - secondTakeProfitRate/100)
|
||||
}
|
||||
|
||||
// 修复错误3:在触发第一止盈时立即调整第二止盈价格
|
||||
if !state.partialTpTriggered && (!math.IsNaN(prevClosePrice) && prevClosePrice <= state.firstTpPrice || lowPrice <= state.firstTpPrice) {
|
||||
state.partialTpTriggered = true
|
||||
state.secondTpPrice = lowPrice * (1 - 0.04) // 基于当前最低价再下跌4%
|
||||
}
|
||||
}
|
||||
|
||||
// 多头平仓条件
|
||||
if exitLong && state.isLong {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
state.isLong = false
|
||||
state.partialTpTriggered = false
|
||||
}
|
||||
|
||||
// 空头平仓条件
|
||||
if exitShort && state.isShort {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
state.isShort = false
|
||||
state.partialTpTriggered = false
|
||||
}
|
||||
},
|
||||
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
|
||||
state, _ := s.More.(*StrategyState)
|
||||
prevClosePrice := s.Env.Close.Get(1)
|
||||
highPrice := s.Env.High.Get(0)
|
||||
lowPrice := s.Env.Low.Get(0)
|
||||
|
||||
if od.Short {
|
||||
// 空仓管理
|
||||
if state.isShort {
|
||||
// 修复错误7:使用前一根K线的收盘价和当前K线的最高价/最低价检查止损
|
||||
if (!math.IsNaN(prevClosePrice) && prevClosePrice >= state.stopPrice) || highPrice >= state.stopPrice {
|
||||
return &strat.ExitReq{
|
||||
Tag: "short_stop_loss",
|
||||
OrderID: od.ID,
|
||||
}
|
||||
}
|
||||
|
||||
// 修复错误7:使用前一根K线的收盘价和当前K线的最低价检查第一止盈
|
||||
if !state.partialTpTriggered && ((!math.IsNaN(prevClosePrice) && prevClosePrice <= state.firstTpPrice) || lowPrice <= state.firstTpPrice) {
|
||||
return &strat.ExitReq{
|
||||
Tag: "short_first_take_profit",
|
||||
OrderID: od.ID,
|
||||
Amount: state.firstTpQty,
|
||||
}
|
||||
}
|
||||
|
||||
// 修复错误7:使用前一根K线的收盘价和当前K线的最低价检查第二止盈
|
||||
if state.partialTpTriggered && ((!math.IsNaN(prevClosePrice) && prevClosePrice <= state.secondTpPrice) || lowPrice <= state.secondTpPrice) {
|
||||
state.isShort = false
|
||||
state.partialTpTriggered = false
|
||||
return &strat.ExitReq{
|
||||
Tag: "short_second_take_profit",
|
||||
OrderID: od.ID,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 多仓管理
|
||||
if state.isLong {
|
||||
// 修复错误7:使用前一根K线的收盘价和当前K线的最低价检查止损
|
||||
if (!math.IsNaN(prevClosePrice) && prevClosePrice <= state.stopPrice) || lowPrice <= state.stopPrice {
|
||||
return &strat.ExitReq{
|
||||
Tag: "long_stop_loss",
|
||||
OrderID: od.ID,
|
||||
}
|
||||
}
|
||||
|
||||
// 修复错误7:使用前一根K线的收盘价和当前K线的最高价检查第一止盈
|
||||
if !state.partialTpTriggered && ((!math.IsNaN(prevClosePrice) && prevClosePrice >= state.firstTpPrice) || highPrice >= state.firstTpPrice) {
|
||||
return &strat.ExitReq{
|
||||
Tag: "long_first_take_profit",
|
||||
OrderID: od.ID,
|
||||
Amount: state.firstTpQty,
|
||||
}
|
||||
}
|
||||
|
||||
// 修复错误7:使用前一根K线的收盘价和当前K线的最高价检查第二止盈
|
||||
if state.partialTpTriggered && ((!math.IsNaN(prevClosePrice) && prevClosePrice >= state.secondTpPrice) || highPrice >= state.secondTpPrice) {
|
||||
state.isLong = false
|
||||
state.partialTpTriggered = false
|
||||
return &strat.ExitReq{
|
||||
Tag: "long_second_take_profit",
|
||||
OrderID: od.ID,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) {
|
||||
state, _ := s.More.(*StrategyState)
|
||||
|
||||
// 订单状态变化时更新策略状态
|
||||
if chgType == strat.OdChgExitFill {
|
||||
if od.Short {
|
||||
// 空单完全平仓
|
||||
state.isShort = false
|
||||
state.partialTpTriggered = false
|
||||
} else {
|
||||
// 多单完全平仓
|
||||
state.isLong = false
|
||||
state.partialTpTriggered = false
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
116
llmjob/199.go
Normal file
116
llmjob/199.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
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 J199(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
emaShortPeriod := pol.DefInt("emaShortPeriod", 12, core.PNorm(5, 20))
|
||||
emaPeriodRatio := pol.Def("emaPeriodRatio", 2.5, core.PNorm(1.5, 4))
|
||||
rsiOverbought := pol.DefInt("rsiOverbought", 70, core.PNorm(60, 85))
|
||||
rsiOversold := pol.DefInt("rsiOversold", 30, core.PNorm(15, 40))
|
||||
|
||||
// 计算长期EMA周期
|
||||
emaLongPeriod := int(float64(emaShortPeriod) * emaPeriodRatio)
|
||||
|
||||
// MACD参数
|
||||
macdFastLength := 12
|
||||
macdSlowLength := 26
|
||||
macdSignalSmoothing := 9
|
||||
|
||||
// RSI参数
|
||||
rsiLength := 14
|
||||
|
||||
// 成交量移动平均周期
|
||||
volumeMAPeriod := 20
|
||||
|
||||
// 计算预热数量,取所有指标中最大的周期
|
||||
warmupNum := emaLongPeriod
|
||||
if macdSlowLength > warmupNum {
|
||||
warmupNum = macdSlowLength
|
||||
}
|
||||
if rsiLength > warmupNum {
|
||||
warmupNum = rsiLength
|
||||
}
|
||||
if volumeMAPeriod > warmupNum {
|
||||
warmupNum = volumeMAPeriod
|
||||
}
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"4h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算技术指标
|
||||
emaShort := ta.EMA(e.Close, emaShortPeriod)
|
||||
emaLong := ta.EMA(e.Close, emaLongPeriod)
|
||||
|
||||
macdLine, signalLine := ta.MACD(e.Close, macdFastLength, macdSlowLength, macdSignalSmoothing)
|
||||
|
||||
rsi := ta.RSI(e.Close, rsiLength)
|
||||
|
||||
volumeMA := ta.SMA(e.Volume, volumeMAPeriod)
|
||||
|
||||
// 获取当前价格
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 生成交易信号 - 修复重复计算EMA交叉的问题
|
||||
emaCross := emaShort.Cross(emaLong) // 只计算一次EMA交叉
|
||||
emaCrossUp := emaCross == 1 // 短期EMA上穿长期EMA
|
||||
emaCrossDown := emaCross == -1 // 短期EMA下穿长期EMA
|
||||
macdBullish := macdLine.Get(0) > signalLine.Get(0) // MACD多头信号
|
||||
macdBearish := macdLine.Get(0) < signalLine.Get(0) // MACD空头信号
|
||||
rsiNotOverbought := rsi.Get(0) < float64(rsiOverbought) // RSI未超买
|
||||
rsiNotOversold := rsi.Get(0) > float64(rsiOversold) // RSI未超卖
|
||||
volumeHigh := e.Volume.Get(0) > volumeMA.Get(0) // 成交量高于平均
|
||||
|
||||
// 交易条件判断
|
||||
buyCondition := emaCrossUp && macdBullish && rsiNotOverbought && volumeHigh
|
||||
sellCondition := emaCrossDown && macdBearish && rsiNotOversold && volumeHigh
|
||||
|
||||
// 计算止损止盈价格 - 修复为使用固定比例,与原始策略一致
|
||||
//longStopLoss := closePrice * 0.95
|
||||
//longTakeProfit := closePrice * 1.05
|
||||
shortStopLoss := closePrice * 1.05
|
||||
shortTakeProfit := closePrice * 0.95
|
||||
|
||||
// 执行交易
|
||||
_ = buyCondition
|
||||
//if buyCondition && len(s.LongOrders) == 0 {
|
||||
// s.OpenOrder(&strat.EnterReq{
|
||||
// Tag: "Buy",
|
||||
// OrderType: core.OrderTypeMarket,
|
||||
// StopLoss: longStopLoss,
|
||||
// TakeProfit: longTakeProfit,
|
||||
// StopLossTag: "Stop Loss",
|
||||
// TakeProfitTag: "Take Profit",
|
||||
// })
|
||||
//}
|
||||
|
||||
if sellCondition && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Sell",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLoss: shortStopLoss,
|
||||
TakeProfit: shortTakeProfit,
|
||||
StopLossTag: "Stop Loss",
|
||||
TakeProfitTag: "Take Profit",
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
68
llmjob/2251.go
Normal file
68
llmjob/2251.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J2251(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastPeriod := pol.DefInt("fastPeriod", 21, core.PNorm(5, 50))
|
||||
slowPeriod := pol.DefInt("slowPeriod", 55, core.PNorm(30, 100))
|
||||
slMultiplier := pol.Def("slMultiplier", 1.5, core.PNorm(0.8, 3))
|
||||
tpMultiplier := pol.Def("tpMultiplier", 1, core.PNorm(0.5, 2.5))
|
||||
atrLength := pol.DefInt("atrLength", 14, core.PNorm(7, 30))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowPeriod, // 预热数量取较长的EMA周期
|
||||
HedgeOff: true, // 单向持仓模式,与PineScript一致
|
||||
RunTimeFrames: []string{"4h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算技术指标
|
||||
fastMA := ta.EMA(e.Close, fastPeriod)
|
||||
slowMA := ta.EMA(e.Close, slowPeriod)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrLength)
|
||||
|
||||
// 获取最新值
|
||||
atrVal := atr.Get(0)
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 检测EMA交叉信号
|
||||
//goLong := fastMA.Cross(slowMA) == 1
|
||||
goShort := fastMA.Cross(slowMA) == -1
|
||||
|
||||
// 金叉信号:做多
|
||||
//if goLong && s.OrderNum == 0 {
|
||||
// // 计算止损和止盈价格
|
||||
// slPrice := closePrice - atrVal*slMultiplier
|
||||
// tpPrice := closePrice + atrVal*tpMultiplier*2
|
||||
// s.OpenOrder(&strat.EnterReq{
|
||||
// Tag: "Long",
|
||||
// Short: false,
|
||||
// Limit: closePrice - atrVal*slMultiplier*0.7,
|
||||
// StopBars: 7,
|
||||
// StopLoss: slPrice, // 做多止损价格
|
||||
// TakeProfit: tpPrice, // 做多止盈价格
|
||||
// })
|
||||
//}
|
||||
|
||||
// 死叉信号:做空
|
||||
if goShort && s.OrderNum == 0 {
|
||||
// 计算止损和止盈价格
|
||||
slPrice := closePrice + atrVal*slMultiplier
|
||||
tpPrice := closePrice - atrVal*tpMultiplier
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
StopLoss: slPrice, // 做空止损价格
|
||||
TakeProfit: tpPrice, // 做空止盈价格
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
174
llmjob/2391.go
Normal file
174
llmjob/2391.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
// Sta2391 用于保存每个品种的策略状态
|
||||
type Sta2391 struct {
|
||||
posReversal123 float64 // 123反转策略的信号值
|
||||
posmStochCross float64 // 随机指标交叉策略的信号值
|
||||
}
|
||||
|
||||
func J2391(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 123反转策略参数
|
||||
length := pol.DefInt("length", 40, core.PNorm(5, 30))
|
||||
kSmoothing := pol.DefInt("kSmoothing", 1, core.PNorm(1, 5))
|
||||
dLength := pol.DefInt("dLength", 3, core.PNorm(1, 5))
|
||||
level := pol.Def("level", 30, core.PNorm(30, 70))
|
||||
|
||||
// 随机指标交叉策略参数
|
||||
lengthSC := pol.DefInt("lengthSC", 20, core.PNorm(3, 21))
|
||||
dLengthSC := pol.DefInt("dLengthSC", 3, core.PNorm(1, 5))
|
||||
oversold := pol.Def("oversold", 10, core.PNorm(10, 30))
|
||||
overbought := pol.Def("overbought", 90, core.PNorm(70, 90))
|
||||
reverse := pol.Def("reverse", 0, core.PUniform(0, 1)) // 0表示不反转,1表示反转
|
||||
|
||||
// 计算预热数量,取两个策略中需要的历史数据最大值
|
||||
warmupNum := length
|
||||
if lengthSC > warmupNum {
|
||||
warmupNum = lengthSC
|
||||
}
|
||||
if kSmoothing+dLength > warmupNum {
|
||||
warmupNum = kSmoothing + dLength
|
||||
}
|
||||
if dLengthSC > warmupNum {
|
||||
warmupNum = dLengthSC
|
||||
}
|
||||
// 额外需要2个历史K线判断价格反转
|
||||
warmupNum += 2
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
TimeFrames: "8h",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略状态
|
||||
s.More = &Sta2391{
|
||||
posReversal123: 0,
|
||||
posmStochCross: 0,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*Sta2391)
|
||||
|
||||
// 获取价格数据
|
||||
close0 := e.Close.Get(0) // 当前收盘价
|
||||
close1 := e.Close.Get(1) // 前一个收盘价
|
||||
close2 := e.Close.Get(2) // 前两个收盘价
|
||||
|
||||
// 1. 计算123反转策略信号
|
||||
// 计算随机指标
|
||||
kFast := ta.Stoch(e.High, e.Low, e.Close, length)
|
||||
// 对K值进行平滑
|
||||
vFast := ta.SMA(kFast, kSmoothing)
|
||||
// 计算D值
|
||||
vSlow := ta.SMA(vFast, dLength)
|
||||
|
||||
// 获取当前值
|
||||
vFastVal := vFast.Get(0)
|
||||
vSlowVal := vSlow.Get(0)
|
||||
|
||||
// 判断123反转策略信号
|
||||
if close2 < close1 && close1 < close0 && vFastVal < vSlowVal && vFastVal > level {
|
||||
// 买入信号:两天内收盘价从低点转为高点,且随机指标快速线在慢速线下方但高于阈值
|
||||
state.posReversal123 = 1
|
||||
} else if close2 > close1 && close1 > close0 && vFastVal > vSlowVal && vFastVal < level {
|
||||
// 卖出信号:两天内收盘价从高点转为低点,且随机指标快速线在慢速线上方但低于阈值
|
||||
state.posReversal123 = -1
|
||||
}
|
||||
// 否则保持之前的信号(不需要else,因为不改变state.posReversal123的值)
|
||||
|
||||
// 2. 计算随机指标交叉策略信号
|
||||
// 计算随机指标
|
||||
kFastSC := ta.Stoch(e.High, e.Low, e.Close, lengthSC)
|
||||
// 计算D值
|
||||
vSlowSC := ta.SMA(kFastSC, dLengthSC)
|
||||
|
||||
// 获取当前值
|
||||
vFastSCVal := kFastSC.Get(0)
|
||||
vSlowSCVal := vSlowSC.Get(0)
|
||||
|
||||
// 判断随机指标交叉策略信号
|
||||
if vFastSCVal >= vSlowSCVal && vFastSCVal < oversold && vSlowSCVal < oversold {
|
||||
// 买入信号:快速线在慢速线上方,且两者都处于超卖区域
|
||||
state.posmStochCross = 1
|
||||
} else if vFastSCVal < vSlowSCVal && vFastSCVal > overbought && vSlowSCVal > overbought {
|
||||
// 卖出信号:快速线在慢速线下方,且两者都处于超买区域
|
||||
state.posmStochCross = -1
|
||||
}
|
||||
// 否则保持之前的信号(不需要else,因为不改变state.posmStochCross的值)
|
||||
|
||||
// 3. 组合信号
|
||||
var pos float64
|
||||
if state.posReversal123 == 1 && state.posmStochCross == 1 {
|
||||
// 两个策略都给出买入信号
|
||||
pos = 1
|
||||
} else if state.posReversal123 == -1 && state.posmStochCross == -1 {
|
||||
// 两个策略都给出卖出信号
|
||||
pos = -1
|
||||
} else {
|
||||
// 信号不一致,无交易信号
|
||||
pos = 0
|
||||
}
|
||||
|
||||
// 4. 处理信号反转
|
||||
var possig float64
|
||||
if reverse == 1 { // 明确检查reverse是否为1
|
||||
if pos == 1 {
|
||||
possig = -1 // 反转买入信号为卖出信号
|
||||
} else if pos == -1 {
|
||||
possig = 1 // 反转卖出信号为买入信号
|
||||
} else {
|
||||
possig = 0
|
||||
}
|
||||
} else {
|
||||
possig = pos // 不反转信号
|
||||
}
|
||||
|
||||
// 5. 执行交易
|
||||
if possig == 1 {
|
||||
// 买入信号
|
||||
if len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
})
|
||||
}
|
||||
// 如果有做空持仓,平仓
|
||||
if len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
} else if possig == -1 {
|
||||
// 卖出信号
|
||||
if len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
// 如果有做多持仓,平仓
|
||||
if len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
} else if possig == 0 {
|
||||
// 无信号,平仓所有持仓
|
||||
if len(s.LongOrders) > 0 || len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_all",
|
||||
Dirt: core.OdDirtBoth,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
144
llmjob/2439.go
Normal file
144
llmjob/2439.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm/ormo"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WeekendData struct {
|
||||
FridayClose float64
|
||||
HasLong bool
|
||||
TotCost float64
|
||||
TotAmt float64
|
||||
}
|
||||
|
||||
func J2439(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
entryThreshold := pol.Def("entryThreshold", 0.02, core.PNorm(0.01, 0.1))
|
||||
profitTakingPercentThreshold := pol.Def("profitTakingPercentThreshold", 0.06, core.PNorm(0.01, 0.08))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: 7, // 需要最多7天的数据来获取周五收盘价
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"5m"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &WeekendData{FridayClose: math.NaN()}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
data, _ := s.More.(*WeekendData)
|
||||
currentTime := time.Unix(e.TimeStop/1000, 0)
|
||||
weekday := currentTime.Weekday()
|
||||
|
||||
// 计算距离周五的天数
|
||||
var daysSinceFriday int
|
||||
switch weekday {
|
||||
case time.Saturday:
|
||||
daysSinceFriday = 1
|
||||
case time.Sunday:
|
||||
daysSinceFriday = 0
|
||||
case time.Monday:
|
||||
daysSinceFriday = 6
|
||||
case time.Tuesday:
|
||||
daysSinceFriday = 5
|
||||
case time.Wednesday:
|
||||
daysSinceFriday = 4
|
||||
case time.Thursday:
|
||||
daysSinceFriday = 3
|
||||
case time.Friday:
|
||||
daysSinceFriday = 2
|
||||
}
|
||||
|
||||
// 获取周五收盘价 - 使用日线数据
|
||||
if e.Close.Len() > daysSinceFriday {
|
||||
data.FridayClose = e.Close.Get(daysSinceFriday)
|
||||
}
|
||||
|
||||
// 如果没有有效的周五收盘价,不执行交易逻辑
|
||||
if math.IsNaN(data.FridayClose) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查止盈条件
|
||||
if s.EnteredNum > 0 {
|
||||
totalProfitRate := 0.0
|
||||
for _, od := range s.LongOrders {
|
||||
if od.Status >= ormo.InOutStatusPartEnter {
|
||||
totalProfitRate += od.ProfitRate
|
||||
}
|
||||
}
|
||||
for _, od := range s.ShortOrders {
|
||||
if od.Status >= ormo.InOutStatusPartEnter {
|
||||
totalProfitRate += od.ProfitRate
|
||||
}
|
||||
}
|
||||
|
||||
if totalProfitRate > profitTakingPercentThreshold {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "profit_take",
|
||||
Dirt: core.OdDirtBoth,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 周末交易逻辑(周日和周一)
|
||||
if weekday == time.Sunday || weekday == time.Monday {
|
||||
currentPrice := e.Close.Get(0)
|
||||
if math.IsNaN(currentPrice) {
|
||||
return
|
||||
}
|
||||
|
||||
upperThreshold := data.FridayClose * (1 + entryThreshold)
|
||||
lowerThreshold := data.FridayClose * (1 - entryThreshold)
|
||||
|
||||
// 没有持仓时的入场逻辑
|
||||
if s.OrderNum == 0 {
|
||||
if currentPrice > upperThreshold {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short_entry",
|
||||
Short: true,
|
||||
})
|
||||
} else if currentPrice < lowerThreshold {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long_entry",
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 已有持仓时的加仓逻辑
|
||||
for _, od := range s.ShortOrders {
|
||||
if od.Status >= ormo.InOutStatusPartEnter && currentPrice > od.InitPrice*(1+entryThreshold) {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "add_short",
|
||||
Short: true,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, od := range s.LongOrders {
|
||||
if od.Status >= ormo.InOutStatusPartEnter {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "add_long",
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 周二平仓逻辑
|
||||
if weekday == time.Tuesday {
|
||||
if s.OrderNum > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "monday_close",
|
||||
Dirt: core.OdDirtBoth,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
103
llmjob/2523.go
Normal file
103
llmjob/2523.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
)
|
||||
|
||||
/*
|
||||
以大止损实现的高胜率,订单太少
|
||||
尝试使用atrBase针对CostRate、TakeProfit、StopLoss进行自适应,效果都更差。
|
||||
*/
|
||||
|
||||
func J2523(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
maLen := pol.DefInt("maLen", 240, core.PNorm(50, 300))
|
||||
adxThres := pol.Def("adxThres", 20, core.PNorm(15, 35))
|
||||
slRate := pol.Def("slRate", 2.5, core.PNorm(0.8, 5.0))
|
||||
|
||||
// 计算预热K线数,确保所有指标都能正确计算
|
||||
warmupNum := maLen + 20
|
||||
|
||||
//rates := make([]float64, 0, 10)
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
RunTimeFrames: []string{"3d"},
|
||||
StakeRate: 20,
|
||||
EachMaxLong: 1,
|
||||
EachMaxShort: 1,
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算技术指标
|
||||
maBig := ta.SMA(e.Close, maLen)
|
||||
rsi := ta.RSI(e.Close, 14)
|
||||
// 使用DMI计算ADX,与原始策略保持一致
|
||||
adx := ta.ADX(e.High, e.Low, e.Close, 14)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, 14)
|
||||
|
||||
// 获取当前值和历史值,避免重复计算
|
||||
curClose := e.Close.Get(0)
|
||||
curMA := maBig.Get(0)
|
||||
curRSI := rsi.Get(0)
|
||||
curADX := adx.Get(0)
|
||||
curATR := atr.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(curClose) || math.IsNaN(curMA) || math.IsNaN(curRSI) ||
|
||||
math.IsNaN(curADX) || math.IsNaN(curATR) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查2根K线前是否发生交叉
|
||||
closeXBig := e.Close.Cross(maBig)
|
||||
rsi2 := rsi.Get(2)
|
||||
adxOk := adx.Get(2) > adxThres
|
||||
buyConfirmation := closeXBig == 3 && (rsi2 > 40) && adxOk
|
||||
sellConfirmation := closeXBig == -3 && (rsi2 < 60) && adxOk
|
||||
|
||||
// 执行交易逻辑
|
||||
if buyConfirmation {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
TakeProfitVal: curClose * 0.02, // 2%止盈
|
||||
StopLossVal: slRate * curATR,
|
||||
})
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "XShort",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
|
||||
if sellConfirmation {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
Short: true,
|
||||
TakeProfitVal: curClose * 0.02, // 2%止盈
|
||||
StopLossVal: slRate * curATR,
|
||||
})
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "XLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
},
|
||||
//OnShutDown: func(s *strat.StratJob) {
|
||||
// if len(rates) == 0 {
|
||||
// return
|
||||
// }
|
||||
// sort.Float64s(rates)
|
||||
// size := len(rates) / 10
|
||||
// for i := 0; i < len(rates); i += size {
|
||||
// block := rates[i:min(len(rates), i+size)]
|
||||
// avg := floats.Sum(block) / float64(len(block))
|
||||
// fmt.Printf("rate %v: %.3f\n", i, avg)
|
||||
// }
|
||||
// rates = nil
|
||||
//},
|
||||
}
|
||||
}
|
||||
168
llmjob/2625.go
Normal file
168
llmjob/2625.go
Normal file
@@ -0,0 +1,168 @@
|
||||
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",
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
104
llmjob/2775.go
Normal file
104
llmjob/2775.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
type MacdRsiState struct {
|
||||
above50 bool // 标记RSI是否上穿50
|
||||
below50 bool // 标记RSI是否下穿50
|
||||
longEntryTime int64 // 记录多头入场时间
|
||||
shortEntryTime int64 // 记录空头入场时间
|
||||
}
|
||||
|
||||
func J2775(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
fastPeriod := pol.DefInt("fastPeriod", 15, core.PNorm(5, 20))
|
||||
macdRate := pol.Def("macdRate", 2.5, core.PNorm(1.5, 4))
|
||||
rsiPeriod := pol.DefInt("rsiPeriod", 16, core.PNorm(7, 21))
|
||||
exitBars := pol.DefInt("exitBars", 5, core.PNorm(1, 10))
|
||||
signalSmoothing := pol.DefInt("signalSmoothing", 9, core.PNorm(5, 15))
|
||||
|
||||
// 计算MACD慢线长度
|
||||
slowPeriod := int(float64(fastPeriod) * macdRate)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowPeriod + signalSmoothing + rsiPeriod,
|
||||
HedgeOff: true, // 单向持仓
|
||||
EachMaxLong: 1, // 最多一个多头订单
|
||||
EachMaxShort: 1, // 最多一个空头订单
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &MacdRsiState{
|
||||
above50: false,
|
||||
below50: false,
|
||||
longEntryTime: 0,
|
||||
shortEntryTime: 0,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*MacdRsiState)
|
||||
|
||||
// 计算MACD指标
|
||||
macdLine, signalLine := ta.MACD(e.Close, fastPeriod, slowPeriod, signalSmoothing)
|
||||
|
||||
// 计算RSI指标
|
||||
rsi := ta.RSI(e.Close, rsiPeriod)
|
||||
|
||||
// 检测MACD金叉和死叉
|
||||
bullishCross := macdLine.Cross(signalLine) == 1
|
||||
bearishCross := macdLine.Cross(signalLine) == -1
|
||||
|
||||
// 更新RSI状态
|
||||
if rsi.Get(0) > 50 && rsi.Get(1) <= 50 {
|
||||
state.above50 = true
|
||||
}
|
||||
if rsi.Get(0) < 50 && rsi.Get(1) >= 50 {
|
||||
state.below50 = true
|
||||
}
|
||||
|
||||
// 执行交易信号
|
||||
if bullishCross && state.above50 && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
})
|
||||
state.longEntryTime = e.TimeStop
|
||||
}
|
||||
|
||||
if bearishCross && state.below50 && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Sell",
|
||||
Short: true,
|
||||
})
|
||||
state.shortEntryTime = e.TimeStop
|
||||
}
|
||||
|
||||
// 检查退出条件
|
||||
if len(s.LongOrders) > 0 && state.longEntryTime > 0 {
|
||||
holdBars := e.BarCount(state.longEntryTime)
|
||||
if holdBars >= float64(exitBars) {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Exit Long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
state.longEntryTime = 0
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.ShortOrders) > 0 && state.shortEntryTime > 0 {
|
||||
holdBars := e.BarCount(state.shortEntryTime)
|
||||
if holdBars >= float64(exitBars) {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Exit Short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
state.shortEntryTime = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
94
llmjob/2874.go
Normal file
94
llmjob/2874.go
Normal file
@@ -0,0 +1,94 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
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 J2874(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
rsiPeriod := pol.DefInt("rsiPeriod", 70, core.PNorm(20, 100))
|
||||
longExitThreshold := pol.Def("longExitThreshold", 0.65, core.PNorm(0.55, 0.8))
|
||||
shortExitThreshold := pol.Def("shortExitThreshold", 0.35, core.PNorm(0.2, 0.45))
|
||||
allowLong := pol.Def("allowLong", 1.0, core.PUniform(0, 1)) > 0.5
|
||||
allowShort := pol.Def("allowShort", 1.0, core.PUniform(0, 1)) > 0.5
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: rsiPeriod, // 预热期至少为RSI周期
|
||||
HedgeOff: true, // 单向持仓模式,与原PineScript策略一致
|
||||
RunTimeFrames: []string{"4h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算各种价格组合
|
||||
ohlc4 := ta.HLC3(e.High, e.Low, e.Close).Add(e.Open).Div(4)
|
||||
hlc3 := ta.HLC3(e.High, e.Low, e.Close)
|
||||
hl2 := ta.HL2(e.High, e.Low)
|
||||
|
||||
// 计算各种RSI值并归一化(除以100)
|
||||
rsiOhlc4 := ta.RSI(ohlc4, rsiPeriod).Div(100)
|
||||
rsiClose := ta.RSI(e.Close, rsiPeriod).Div(100)
|
||||
rsiOpen := ta.RSI(e.Open, rsiPeriod).Div(100)
|
||||
rsiHigh := ta.RSI(e.High, rsiPeriod).Div(100)
|
||||
rsiHlc3 := ta.RSI(hlc3, rsiPeriod).Div(100)
|
||||
rsiHl2 := ta.RSI(hl2, rsiPeriod).Div(100)
|
||||
|
||||
// 计算RSI均值
|
||||
rsiAvg := rsiOhlc4.Add(rsiClose).Add(rsiOpen).Add(rsiHigh).Add(rsiHlc3).Add(rsiHl2).Div(6)
|
||||
rsiAvgVal := rsiAvg.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(rsiAvgVal) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测RSI均值与0.5的交叉
|
||||
entryThreshold := 0.5
|
||||
crossSignal := rsiAvg.Cross(entryThreshold)
|
||||
|
||||
// 生成交易信号
|
||||
longSignal := crossSignal == 1 // RSI均值上穿0.5
|
||||
shortSignal := crossSignal == -1 // RSI均值下穿0.5
|
||||
|
||||
// 生成平仓信号
|
||||
longExit := rsiAvgVal >= longExitThreshold || shortSignal
|
||||
shortExit := rsiAvgVal <= shortExitThreshold || longSignal
|
||||
|
||||
// 执行交易逻辑
|
||||
if allowLong && longSignal && len(s.LongOrders) == 0 {
|
||||
// 开多头仓位
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
})
|
||||
}
|
||||
|
||||
if allowShort && shortSignal && len(s.ShortOrders) == 0 {
|
||||
// 开空头仓位
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
if allowLong && len(s.LongOrders) > 0 && longExit {
|
||||
// 平多头仓位
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "long_exit",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
if allowShort && len(s.ShortOrders) > 0 && shortExit {
|
||||
// 平空头仓位
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "short_exit",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
110
llmjob/2876.go
Normal file
110
llmjob/2876.go
Normal file
@@ -0,0 +1,110 @@
|
||||
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"
|
||||
)
|
||||
|
||||
func J2876(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
frequency := pol.DefInt("frequency", 28, core.PNorm(5, 30))
|
||||
rsiFrequency := pol.DefInt("rsiFrequency", 220, core.PNorm(14, 100))
|
||||
buyZoneDistance := pol.Def("buyZoneDistance", 7, core.PNorm(2, 15))
|
||||
avgDownATRSum := pol.DefInt("avgDownATRSum", 3, core.PNorm(1, 10))
|
||||
useAbsoluteRSIBarrier := pol.Def("useAbsoluteRSIBarrier", 1, core.PUniform(0, 1)) // 1表示true,0表示false
|
||||
barrierLevel := pol.Def("barrierLevel", 45, core.PNorm(30, 70))
|
||||
|
||||
// 计算预热数量,取RSI周期和均线周期中的较大值
|
||||
warmupNum := rsiFrequency
|
||||
if frequency > rsiFrequency {
|
||||
warmupNum = frequency
|
||||
}
|
||||
// 再加上ATR周期
|
||||
warmupNum += 20
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"15m"},
|
||||
EachMaxLong: 3, // 最多允许3个多头订单,对应原策略的pyramiding=3
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算技术指标
|
||||
momentumRSI := ta.RSI(e.Close, rsiFrequency)
|
||||
momentumRSI_slow := ta.SMA(momentumRSI, frequency)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, 20)
|
||||
|
||||
// 获取当前值
|
||||
rsiValue := momentumRSI.Get(0)
|
||||
rsiSlowValue := momentumRSI_slow.Get(0)
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(rsiValue) || math.IsNaN(rsiSlowValue) || math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算买卖区域
|
||||
buyZoneLower := rsiSlowValue * (1 - buyZoneDistance/100)
|
||||
|
||||
// 生成交易信号
|
||||
useBarrier := useAbsoluteRSIBarrier > 0.5
|
||||
momentumRSISoftClose := (rsiValue > rsiSlowValue) && (rsiValue > barrierLevel || !useBarrier)
|
||||
isClose := momentumRSISoftClose
|
||||
|
||||
// 计算风险管理条件
|
||||
riskCondition := true
|
||||
if len(s.LongOrders) > 0 {
|
||||
// 计算平均持仓价格
|
||||
totalCost := 0.0
|
||||
totalAmount := 0.0
|
||||
for _, od := range s.LongOrders {
|
||||
if od.Status >= ormo.InOutStatusPartEnter {
|
||||
totalCost += od.EnterCost()
|
||||
totalAmount += od.HoldAmount()
|
||||
}
|
||||
}
|
||||
if totalAmount > 0 {
|
||||
avgPrice := totalCost / totalAmount
|
||||
// 计算过去avgDownATRSum个周期的ATR值之和
|
||||
atrSum := 0.0
|
||||
for i := 0; i < avgDownATRSum; i++ {
|
||||
atrVal := atr.Get(i)
|
||||
if !math.IsNaN(atrVal) {
|
||||
atrSum += atrVal
|
||||
}
|
||||
}
|
||||
// 计算风险条件:平均持仓价格 - ATR求和值 * 开仓数量 > 当前价格
|
||||
riskCondition = avgPrice-atrSum*float64(len(s.LongOrders)) > closePrice
|
||||
}
|
||||
}
|
||||
|
||||
// 买入信号:RSI低于买入区域下限且满足风险条件
|
||||
isBuy := rsiValue < buyZoneLower && (riskCondition || len(s.LongOrders) == 0)
|
||||
|
||||
// 执行交易操作
|
||||
if isBuy && len(s.LongOrders) < 3 { // 限制最多3个多头订单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
// 平仓信号
|
||||
if isClose && len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Close",
|
||||
Dirt: core.OdDirtLong,
|
||||
OrderType: core.OrderTypeLimit,
|
||||
Limit: closePrice,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
269
llmjob/2984.go
Normal file
269
llmjob/2984.go
Normal file
@@ -0,0 +1,269 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// ZeroLagEMA 计算零滞后指数移动平均线
|
||||
func ZeroLagEMA(source *ta.Series, length int) *ta.Series {
|
||||
res := source.To("_zlema", length)
|
||||
if res.Cached() {
|
||||
return res
|
||||
}
|
||||
if source.Len() < length {
|
||||
return res.Append(math.NaN())
|
||||
}
|
||||
ema1 := ta.EMA(source, length)
|
||||
ema2 := ta.EMA(ema1, length)
|
||||
c1 := ema1.Get(0) - ema2.Get(0)
|
||||
zlema := ema1.Get(0) + c1
|
||||
return res.Append(zlema)
|
||||
}
|
||||
|
||||
// ZlemaState 存储每个品种的策略状态
|
||||
type ZlemaState struct {
|
||||
goLong bool
|
||||
sectionLongs int
|
||||
sectionShorts int
|
||||
sectionLongConditions int
|
||||
sectionShortConditions int
|
||||
totalLongs float64
|
||||
totalShorts float64
|
||||
averageLongs float64
|
||||
averageShorts float64
|
||||
lastHigh float64
|
||||
lastLow float64
|
||||
inLong bool
|
||||
inShort bool
|
||||
}
|
||||
|
||||
func J2984(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastPeriod := pol.DefInt("fastPeriod", 10, core.PNorm(3, 20))
|
||||
maRate := pol.Def("maRate", 2.4, core.PNorm(1.5, 5))
|
||||
takeProfitRate := pol.Def("takeProfitRate", 3.5, core.PNorm(1, 10))
|
||||
trailingStopRate := pol.Def("trailingStopRate", 3.5, core.PNorm(1, 8))
|
||||
trailingActivationRate := pol.Def("trailingActivationRate", 16, core.PNorm(5, 25))
|
||||
stopLossPercent := pol.Def("stopLossPercent", 7.5, core.PNorm(1, 15))
|
||||
|
||||
// 金字塔加仓参数
|
||||
pyramidingLess := pol.DefInt("pyramidingLess", 1, core.PNorm(0, 5))
|
||||
pyramidingEqual := pol.DefInt("pyramidingEqual", 0, core.PNorm(0, 5))
|
||||
pyramidingGreater := pol.DefInt("pyramidingGreater", 1000000, core.PNorm(100, 1000000))
|
||||
|
||||
// 交易方向控制参数
|
||||
longsOnly := pol.Def("longsOnly", 0, core.PUniform(0, 1)) > 0.5
|
||||
shortsOnly := pol.Def("shortsOnly", 0, core.PUniform(0, 1)) > 0.5
|
||||
flipOpens := pol.Def("flipOpens", 0, core.PUniform(0, 1)) > 0.5
|
||||
|
||||
// 计算慢线周期
|
||||
slowPeriod := int(float64(fastPeriod) * maRate)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowPeriod * 2, // 确保有足够的数据计算指标
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多开1单多仓
|
||||
EachMaxShort: 1, // 最多开1单空仓
|
||||
DrawDownExit: false, // 不使用回撤止盈
|
||||
RunTimeFrames: []string{"8h"},
|
||||
StopLoss: 0.075,
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略状态
|
||||
s.More = &ZlemaState{
|
||||
goLong: true,
|
||||
sectionLongs: 0,
|
||||
sectionShorts: 0,
|
||||
sectionLongConditions: 0,
|
||||
sectionShortConditions: 0,
|
||||
totalLongs: 0,
|
||||
totalShorts: 0,
|
||||
averageLongs: 0,
|
||||
averageShorts: 0,
|
||||
lastHigh: 0,
|
||||
lastLow: 0,
|
||||
inLong: false,
|
||||
inShort: false,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*ZlemaState)
|
||||
|
||||
// 计算Zero-Lag EMA
|
||||
zlemaFast := ZeroLagEMA(e.Close, fastPeriod)
|
||||
zlemaSlow := ZeroLagEMA(e.Close, slowPeriod)
|
||||
|
||||
// 获取当前值
|
||||
fastVal := zlemaFast.Get(0)
|
||||
slowVal := zlemaSlow.Get(0)
|
||||
fastVal1 := zlemaFast.Get(1)
|
||||
slowVal1 := zlemaSlow.Get(1)
|
||||
closePrice := e.Close.Get(0)
|
||||
highPrice := e.High.Get(0)
|
||||
lowPrice := e.Low.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(fastVal) || math.IsNaN(slowVal) || math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 生成基础交易信号
|
||||
longSignal := fastVal > slowVal && fastVal1 <= slowVal1 // 快线上穿慢线
|
||||
shortSignal := fastVal < slowVal && fastVal1 >= slowVal1 // 快线下穿慢线
|
||||
|
||||
// 应用交易方向翻转功能
|
||||
if flipOpens {
|
||||
longSignal, shortSignal = shortSignal, longSignal
|
||||
}
|
||||
|
||||
// 应用仅做多/仅做空选项
|
||||
if longsOnly {
|
||||
shortSignal = false
|
||||
}
|
||||
if shortsOnly {
|
||||
longSignal = false
|
||||
}
|
||||
|
||||
// 更新连续信号计数
|
||||
if longSignal {
|
||||
state.sectionLongs++
|
||||
state.sectionShorts = 0
|
||||
} else if shortSignal {
|
||||
state.sectionShorts++
|
||||
state.sectionLongs = 0
|
||||
}
|
||||
|
||||
// 应用金字塔式加仓条件
|
||||
longCondition := longSignal && (state.sectionLongs <= pyramidingLess || state.sectionLongs >= pyramidingGreater || state.sectionLongs == pyramidingEqual)
|
||||
shortCondition := shortSignal && (state.sectionShorts <= pyramidingLess || state.sectionShorts >= pyramidingGreater || state.sectionShorts == pyramidingEqual)
|
||||
|
||||
// 更新开仓价格和数量
|
||||
if longCondition {
|
||||
state.totalLongs += closePrice
|
||||
state.sectionLongConditions++
|
||||
state.averageLongs = state.totalLongs / float64(state.sectionLongConditions)
|
||||
state.inLong = true
|
||||
state.inShort = false
|
||||
} else if shortCondition {
|
||||
state.totalShorts += closePrice
|
||||
state.sectionShortConditions++
|
||||
state.averageShorts = state.totalShorts / float64(state.sectionShortConditions)
|
||||
state.inShort = true
|
||||
state.inLong = false
|
||||
}
|
||||
|
||||
// 更新最高价/最低价追踪
|
||||
if state.inLong {
|
||||
if state.lastHigh == 0 || highPrice > state.lastHigh {
|
||||
state.lastHigh = highPrice
|
||||
}
|
||||
} else if state.inShort {
|
||||
if state.lastLow == 0 || lowPrice < state.lastLow {
|
||||
state.lastLow = lowPrice
|
||||
}
|
||||
}
|
||||
|
||||
// 执行交易
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
// 设置止损止盈 - 基于平均开仓价计算
|
||||
stopLossVal := state.averageLongs * (1 - stopLossPercent/100)
|
||||
takeProfitVal := state.averageLongs * (1 + takeProfitRate/100)
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: stopLossVal,
|
||||
TakeProfitVal: takeProfitVal,
|
||||
StopLossTag: "stop_loss",
|
||||
TakeProfitTag: "take_profit",
|
||||
})
|
||||
} else if shortCondition && len(s.ShortOrders) == 0 {
|
||||
// 设置止损止盈 - 基于平均开仓价计算
|
||||
stopLossVal := state.averageShorts * (1 + stopLossPercent/100)
|
||||
takeProfitVal := state.averageShorts * (1 - takeProfitRate/100)
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: stopLossVal,
|
||||
TakeProfitVal: takeProfitVal,
|
||||
StopLossTag: "stop_loss",
|
||||
TakeProfitTag: "take_profit",
|
||||
})
|
||||
}
|
||||
|
||||
// 检查平仓条件
|
||||
if state.inLong && shortSignal {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "trend_reverse",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
} else if state.inShort && longSignal {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "trend_reverse",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*ZlemaState)
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
if od.Short {
|
||||
// 做空订单检查
|
||||
if state.inShort {
|
||||
// 检查追踪止损条件 - 包含激活条件判断
|
||||
if state.lastLow > 0 &&
|
||||
state.lastLow <= state.averageShorts*(1-trailingActivationRate/100) && // 激活条件
|
||||
closePrice >= state.lastLow*(1+trailingStopRate/100) { // 追踪止损条件
|
||||
return &strat.ExitReq{Tag: "trailing_stop"}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 做多订单检查
|
||||
if state.inLong {
|
||||
// 检查追踪止损条件 - 包含激活条件判断
|
||||
if state.lastHigh > 0 &&
|
||||
state.lastHigh >= state.averageLongs*(1+trailingActivationRate/100) && // 激活条件
|
||||
closePrice <= state.lastHigh*(1-trailingStopRate/100) { // 追踪止损条件
|
||||
return &strat.ExitReq{Tag: "trailing_stop"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) {
|
||||
state, _ := s.More.(*ZlemaState)
|
||||
|
||||
// 订单状态变化时更新状态
|
||||
if chgType == strat.OdChgExitFill {
|
||||
if od.Short {
|
||||
// 做空订单已平仓
|
||||
state.inShort = false
|
||||
state.averageShorts = 0
|
||||
state.totalShorts = 0
|
||||
state.sectionShorts = 0
|
||||
state.sectionShortConditions = 0
|
||||
state.lastLow = 0
|
||||
} else {
|
||||
// 做多订单已平仓
|
||||
state.inLong = false
|
||||
state.averageLongs = 0
|
||||
state.totalLongs = 0
|
||||
state.sectionLongs = 0
|
||||
state.sectionLongConditions = 0
|
||||
state.lastHigh = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
154
llmjob/3087.go
Normal file
154
llmjob/3087.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"gonum.org/v1/gonum/floats"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
/*
|
||||
基于初始的3087进行了简化,只保留盈利较好的14-26
|
||||
*/
|
||||
|
||||
// DCAStrategyState 存储每个品种的策略状态
|
||||
type DCAStrategyState struct {
|
||||
currentSO int // 当前安全订单计数器
|
||||
firstPrice float64 // 初始订单价格
|
||||
totalQtys float64 // 总交易数量
|
||||
totalCost float64 // 累计花费
|
||||
realTotQtys float64 // 总交易数量
|
||||
realTotCost float64 // 累计花费
|
||||
}
|
||||
|
||||
// calculateSafetyOrderThreshold 计算安全订单触发价格阈值
|
||||
func calculateSafetyOrderThreshold(initPrice, priceDeviation, stepScale float64, currentSO int) float64 {
|
||||
curSO := float64(currentSO)
|
||||
if stepScale == 1.0 {
|
||||
return initPrice - initPrice*priceDeviation*stepScale*curSO
|
||||
}
|
||||
rate := priceDeviation*math.Pow(stepScale, curSO) - priceDeviation
|
||||
return initPrice - initPrice*(rate/(stepScale-1))
|
||||
}
|
||||
|
||||
// DynamicDCA 动态DCA策略
|
||||
func J3087(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
priceDeviation := pol.Def("priceDeviation", 0.82, core.PNorm(0.5, 3.0))
|
||||
volumeScale := pol.Def("volumeScale", 1.16, core.PNorm(1.05, 1.5))
|
||||
stepScale := pol.Def("stepScale", 1.06, core.PNorm(1.05, 1.3))
|
||||
takeProfit := pol.Def("takeProfit", 1.15, core.PNorm(0.5, 2.5))
|
||||
baseOrder := pol.Def("baseOrder", 10.0, core.PNorm(5, 20))
|
||||
startLvl := pol.DefInt("startLvl", 14.0, core.PNorm(1, 20))
|
||||
endLvl := pol.DefInt("endLvl", 26.0, core.PNorm(3, 30))
|
||||
maxSafeOrders := pol.DefInt("maxSafeOrders", 30, core.PNorm(5, 50))
|
||||
|
||||
lenAtr := 20
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
var rates []float64
|
||||
// 14/15/16/17/18/19/20/21/22/23/24/25/26
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: 1,
|
||||
HedgeOff: true, // 单向持仓
|
||||
RunTimeFrames: []string{"15m"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略状态
|
||||
s.More = &DCAStrategyState{}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*DCAStrategyState)
|
||||
|
||||
// 计算OHLC4加权均价
|
||||
price := (e.High.Get(0) + e.Low.Get(0) + e.Close.Get(0) + e.Open.Get(0)) / 4
|
||||
closeP := e.Close.Get(0)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
|
||||
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
|
||||
openRate := 0.004 * closeP / atrBase
|
||||
if openRate < 1 {
|
||||
openRate = math.Sqrt(openRate)
|
||||
}
|
||||
openRate = max(0.7, min(1.5, openRate))
|
||||
|
||||
// 计算止盈水平
|
||||
hasOrders := state.currentSO > 0
|
||||
var avgPrice, realAvgPrice float64
|
||||
if hasOrders && state.totalQtys > 0 {
|
||||
avgPrice = state.totalCost / state.totalQtys
|
||||
realAvgPrice = state.realTotCost / state.realTotQtys
|
||||
}
|
||||
takeProfitLevel := avgPrice * (1 + takeProfit/100)
|
||||
|
||||
// 初始建仓逻辑
|
||||
if state.currentSO == 0 && price > 0 {
|
||||
// 计算买入数量
|
||||
qty := baseOrder / price
|
||||
// 首次买入表现不佳,禁止实际买入
|
||||
state.totalQtys += qty
|
||||
state.totalCost += qty * closeP
|
||||
state.firstPrice = price
|
||||
state.currentSO = 1
|
||||
}
|
||||
|
||||
// 计算安全订单触发价格阈值
|
||||
priceRate := priceDeviation / 100
|
||||
threshold := calculateSafetyOrderThreshold(state.firstPrice, priceRate, stepScale, state.currentSO)
|
||||
|
||||
// 动态补仓逻辑
|
||||
if hasOrders && price <= threshold && state.currentSO <= maxSafeOrders {
|
||||
// 计算补仓数量
|
||||
orderQty := baseOrder * openRate * math.Pow(volumeScale, float64(state.currentSO-1)) / price
|
||||
|
||||
// endLvl用于实际下单。maxSafeOrders用于止盈价格
|
||||
if state.currentSO >= startLvl && state.currentSO <= endLvl {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: fmt.Sprintf("%v", state.currentSO),
|
||||
Amount: orderQty,
|
||||
Log: true,
|
||||
})
|
||||
state.realTotQtys += orderQty
|
||||
state.realTotCost += orderQty * closeP
|
||||
}
|
||||
|
||||
state.totalQtys += orderQty
|
||||
state.totalCost += orderQty * closeP
|
||||
state.currentSO += 1
|
||||
}
|
||||
|
||||
// 止盈平仓逻辑
|
||||
if price >= takeProfitLevel && hasOrders {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TakeProfit",
|
||||
})
|
||||
if realAvgPrice > 0 {
|
||||
rates = append(rates, takeProfitLevel/realAvgPrice)
|
||||
}
|
||||
|
||||
// 重置状态变量
|
||||
state.currentSO = 0
|
||||
state.totalQtys = 0.0
|
||||
state.totalCost = 0
|
||||
state.realTotQtys = 0
|
||||
state.realTotCost = 0
|
||||
state.firstPrice = 0
|
||||
}
|
||||
},
|
||||
OnShutDown: func(s *strat.StratJob) {
|
||||
if len(rates) == 0 {
|
||||
return
|
||||
}
|
||||
sort.Float64s(rates)
|
||||
step := len(rates) / 7
|
||||
for i := 0; i < len(rates); i += step {
|
||||
block := rates[i:min(len(rates), i+step)]
|
||||
avg := floats.Sum(block) / float64(len(block))
|
||||
fmt.Printf("rate: %v: %v\n", i, avg)
|
||||
}
|
||||
rates = nil
|
||||
},
|
||||
}
|
||||
}
|
||||
88
llmjob/3121.go
Normal file
88
llmjob/3121.go
Normal file
@@ -0,0 +1,88 @@
|
||||
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 J3121(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
bbLength := pol.DefInt("bbLength", 20, core.PNorm(10, 50))
|
||||
bbStdDev := pol.Def("bbStdDev", 1.8, core.PNorm(1.0, 3.0))
|
||||
cmoLength := pol.DefInt("cmoLength", 10, core.PNorm(5, 30))
|
||||
cmoOverbought := pol.Def("cmoOverbought", 45.0, core.PNorm(30.0, 70.0))
|
||||
cmoOversold := pol.Def("cmoOversold", -50.0, core.PNorm(-70.0, -30.0))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: bbLength, // 预热期设置为布林带周期长度
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多一个多头订单
|
||||
EachMaxShort: 1, // 最多一个空头订单
|
||||
RunTimeFrames: []string{"5m"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算布林带
|
||||
bbUpper, bbBasis, bbLower := ta.BBANDS(e.Close, bbLength, bbStdDev, bbStdDev)
|
||||
|
||||
// 计算钱德动量振荡器
|
||||
cmo := ta.CMO(e.Close, cmoLength)
|
||||
|
||||
// 获取当前值
|
||||
closePrice := e.Close.Get(0)
|
||||
cmoValue := cmo.Get(0)
|
||||
bbUpperValue := bbUpper.Get(0)
|
||||
bbLowerValue := bbLower.Get(0)
|
||||
bbBasisValue := bbBasis.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(closePrice) || math.IsNaN(cmoValue) ||
|
||||
math.IsNaN(bbUpperValue) || math.IsNaN(bbLowerValue) || math.IsNaN(bbBasisValue) {
|
||||
return
|
||||
}
|
||||
|
||||
// 做多条件:价格下穿布林带下轨且CMO低于超卖阈值
|
||||
longCondition := e.Close.Cross(bbLower) == -1 && cmoValue < cmoOversold
|
||||
|
||||
// 做空条件:价格上穿布林带上轨且CMO高于超买阈值
|
||||
shortCondition := e.Close.Cross(bbUpper) == 1 && cmoValue > cmoOverbought
|
||||
|
||||
// 平多条件:价格上穿布林带中轨或CMO高于超买阈值
|
||||
exitLong := e.Close.Cross(bbBasis) == 1 || cmoValue > cmoOverbought
|
||||
|
||||
// 平空条件:价格下穿布林带中轨或CMO低于超卖阈值
|
||||
exitShort := e.Close.Cross(bbBasis) == -1 || cmoValue < cmoOversold
|
||||
|
||||
// 执行交易操作
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
})
|
||||
}
|
||||
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
if exitLong && len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
if exitShort && len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
55
llmjob/3127.go
Normal file
55
llmjob/3127.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// 修复后的完整策略代码
|
||||
// 请勿修改package包名和任意函数名
|
||||
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 J3127(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
buyThreshold := pol.Def("buyThreshold", 0.025, core.PNorm(0.005, 0.05))
|
||||
sellThreshold := pol.Def("sellThreshold", 0.025, core.PNorm(0.005, 0.05))
|
||||
lookbackPeriod := pol.DefInt("lookbackPeriod", 16, core.PNorm(5, 20))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: lookbackPeriod,
|
||||
HedgeOff: true,
|
||||
EachMaxLong: 1,
|
||||
RunTimeFrames: []string{"15m"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
if math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 修复:使用收盘价计算历史极值
|
||||
highest := ta.Highest(e.Close, lookbackPeriod).Get(0)
|
||||
lowest := ta.Lowest(e.Close, lookbackPeriod).Get(0)
|
||||
|
||||
buyTrigger := highest * (1 - buyThreshold)
|
||||
sellTrigger := lowest * (1 + sellThreshold)
|
||||
|
||||
if closePrice < buyTrigger && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Achat",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
if closePrice > sellTrigger && len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Vente",
|
||||
Dirt: core.OdDirtLong,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
127
llmjob/3137.go
Normal file
127
llmjob/3137.go
Normal file
@@ -0,0 +1,127 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
)
|
||||
|
||||
type DDLState struct {
|
||||
lastbull float64 // 最后一个涨停价(连续上涨时的最高点)
|
||||
lastbear float64 // 最后一个跌停价(连续下跌时的最低点)
|
||||
lot float64 // 计算后的开仓数量
|
||||
}
|
||||
|
||||
func J3137(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
signalshort := pol.Def("signalshort", 5.1, core.PNorm(1.0, 10.0))
|
||||
signallong := pol.Def("signallong", -5.1, core.PNorm(-10.0, -1.0))
|
||||
capital := pol.Def("capital", 145.0, core.PNorm(10.0, 200.0))
|
||||
needlong := pol.Def("needlong", 1.0, core.PNorm(0.0, 1.0))
|
||||
needshort := pol.Def("needshort", 1.0, core.PNorm(0.0, 1.0))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: 2, // 至少需要2个K线数据才能计算bull和bear
|
||||
HedgeOff: false, // 允许双向持仓
|
||||
RunTimeFrames: []string{"15m"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的状态
|
||||
s.More = &DDLState{
|
||||
lastbull: math.NaN(),
|
||||
lastbear: math.NaN(),
|
||||
lot: 0.0,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*DDLState)
|
||||
|
||||
// 获取当前和前一个K线的价格
|
||||
closePrice := e.Close.Get(0)
|
||||
prevClose := e.Close.Get(1)
|
||||
openPrice := e.Open.Get(0)
|
||||
|
||||
// 计算capital比例,避免重复计算
|
||||
capitalRate := capital / 100
|
||||
|
||||
// 如果数据不足,返回
|
||||
if math.IsNaN(closePrice) || math.IsNaN(prevClose) {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算价格波动方向
|
||||
bull := closePrice > prevClose
|
||||
bear := closePrice < prevClose
|
||||
|
||||
// 更新最后一个涨停价和跌停价
|
||||
if bull {
|
||||
state.lastbull = closePrice
|
||||
}
|
||||
if bear {
|
||||
state.lastbear = closePrice
|
||||
}
|
||||
|
||||
// 如果还没有初始化lastbull和lastbear,返回
|
||||
if math.IsNaN(state.lastbull) || math.IsNaN(state.lastbear) {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算价格变化比例
|
||||
ddl := ((closePrice / state.lastbull) - 1) * 100
|
||||
dds := ((closePrice / state.lastbear) - 1) * 100
|
||||
|
||||
// 生成交易信号
|
||||
up := ddl < signallong
|
||||
dn := dds > signalshort
|
||||
|
||||
// 计算开仓数量(如果没有持仓)
|
||||
if s.OrderNum == 0 {
|
||||
// 修复:使用策略方法获取权益计算仓位
|
||||
stakeAmount := s.Strat.GetStakeAmount(s)
|
||||
state.lot = stakeAmount / closePrice * capitalRate
|
||||
}
|
||||
|
||||
// truetime条件,与原始策略保持一致
|
||||
truetime := true
|
||||
|
||||
// 执行交易
|
||||
if up && needlong > 0.5 && truetime {
|
||||
// 修复:使用计算好的lot值作为开仓数量
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Short: false,
|
||||
Amount: state.lot,
|
||||
})
|
||||
}
|
||||
|
||||
if dn && needshort > 0.5 && truetime {
|
||||
// 修复:使用计算好的lot值作为开仓数量
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
Amount: state.lot,
|
||||
})
|
||||
}
|
||||
|
||||
// 平仓条件:开多仓后价格上涨就平多仓,开空仓后价格下跌就平空仓
|
||||
if len(s.LongOrders) > 0 && closePrice > openPrice {
|
||||
// 修复:使用CloseOrders进行平仓
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CloseLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
if len(s.ShortOrders) > 0 && closePrice < openPrice {
|
||||
// 修复:使用CloseOrders进行平仓
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CloseShort",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
64
llmjob/32.go
Normal file
64
llmjob/32.go
Normal file
@@ -0,0 +1,64 @@
|
||||
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 J32(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
smaLength := pol.DefInt("smaLength", 30, core.PNorm(5, 30))
|
||||
macdFastLength := pol.DefInt("macdFastLength", 25, core.PNorm(5, 20))
|
||||
macdSlowLength := pol.DefInt("macdSlowLength", 38, core.PNorm(15, 40))
|
||||
macdSignalSmoothing := pol.DefInt("macdSignalSmoothing", 3, core.PNorm(3, 15))
|
||||
warmupNum := smaLength
|
||||
if macdSlowLength > warmupNum {
|
||||
warmupNum = macdSlowLength
|
||||
}
|
||||
if macdFastLength+macdSignalSmoothing > warmupNum {
|
||||
warmupNum = macdFastLength + macdSignalSmoothing
|
||||
}
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true,
|
||||
EachMaxLong: 1,
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
sma10 := ta.SMA(e.Close, smaLength)
|
||||
macdLine, signalLine := ta.MACD(e.Close, macdFastLength, macdSlowLength, macdSignalSmoothing)
|
||||
closePrice := e.Close.Get(0)
|
||||
sma10Value := sma10.Get(0)
|
||||
macdValue := macdLine.Get(0)
|
||||
signalValue := signalLine.Get(0)
|
||||
if math.IsNaN(closePrice) || math.IsNaN(sma10Value) || math.IsNaN(macdValue) || math.IsNaN(signalValue) {
|
||||
return
|
||||
}
|
||||
priceCrossSMA := e.Close.Cross(sma10)
|
||||
macdCrossSignal := macdLine.Cross(signalLine)
|
||||
longCondition := priceCrossSMA == 1 && macdCrossSignal == 1
|
||||
shortCondition := priceCrossSMA == -1 && macdCrossSignal == -1
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
})
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CloseShort",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
if shortCondition && len(s.LongOrders) > 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
})
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CloseLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
173
llmjob/3464.go
Normal file
173
llmjob/3464.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
)
|
||||
|
||||
type TrendlineData struct {
|
||||
upper float64
|
||||
lower float64
|
||||
slopePh float64
|
||||
slopePl float64
|
||||
upos int
|
||||
dnos int
|
||||
prevUpos int
|
||||
prevDnos int
|
||||
}
|
||||
|
||||
func J3464(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
length := pol.DefInt("length", 13, core.PNorm(5, 30))
|
||||
mult := pol.Def("mult", 1.05, core.PNorm(0.5, 2.0))
|
||||
calcMethod := pol.Def("calcMethod", 0, core.PUniform(0, 2)) // 0: Atr, 1: Stdev, 2: Linreg
|
||||
_ = pol.Def("backpaint", 1.0, core.PUniform(0, 1)) == 1.0 // 使用下划线忽略未使用的变量
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: length * 2,
|
||||
HedgeOff: true, // 单向持仓
|
||||
RunTimeFrames: []string{"4h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &TrendlineData{
|
||||
upper: 0,
|
||||
lower: 0,
|
||||
slopePh: 0,
|
||||
slopePl: 0,
|
||||
upos: 0,
|
||||
dnos: 0,
|
||||
prevUpos: 0,
|
||||
prevDnos: 0,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
data, _ := s.More.(*TrendlineData)
|
||||
|
||||
// 获取当前价格
|
||||
closePrice := e.Close.Get(0)
|
||||
if math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测支点 - 实现与PineScript的pivothigh和pivotlow相似的功能
|
||||
// 需要检查当前K线是否是过去length周期内的最高点,并且左右两侧都有length个K线
|
||||
ph := false
|
||||
pl := false
|
||||
|
||||
// 确保有足够的历史数据
|
||||
if e.Close.Len() > length*2 {
|
||||
// 检查当前K线是否是支点高
|
||||
currentHigh := e.High.Get(length)
|
||||
isPivotHigh := true
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
if e.High.Get(i) >= currentHigh || e.High.Get(length*2-i) >= currentHigh {
|
||||
isPivotHigh = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 检查当前K线是否是支点低
|
||||
currentLow := e.Low.Get(length)
|
||||
isPivotLow := true
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
if e.Low.Get(i) <= currentLow || e.Low.Get(length*2-i) <= currentLow {
|
||||
isPivotLow = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ph = isPivotHigh && !math.IsNaN(currentHigh)
|
||||
pl = isPivotLow && !math.IsNaN(currentLow)
|
||||
}
|
||||
|
||||
// 计算斜率
|
||||
var slope float64
|
||||
method := int(calcMethod)
|
||||
switch method {
|
||||
case 0: // Atr
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, length)
|
||||
slope = atr.Get(0) / float64(length) * mult
|
||||
case 1: // Stdev
|
||||
stdDev := ta.StdDev(e.Close, length)
|
||||
slope = stdDev.Get(0) / float64(length) * mult
|
||||
case 2: // Linreg
|
||||
// 简化版的线性回归斜率计算
|
||||
n := float64(length)
|
||||
sumX := n * (n - 1) / 2
|
||||
sumY := ta.SMA(e.Close, length).Get(0) * n
|
||||
sumXY := 0.0
|
||||
sumX2 := 0.0
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
x := float64(i)
|
||||
y := e.Close.Get(i)
|
||||
if !math.IsNaN(y) {
|
||||
sumXY += x * y
|
||||
sumX2 += x * x
|
||||
}
|
||||
}
|
||||
|
||||
// 计算斜率
|
||||
denominator := n*sumX2 - sumX*sumX
|
||||
if denominator != 0 {
|
||||
slope = (n*sumXY - sumX*sumY) / denominator * mult
|
||||
} else {
|
||||
slope = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 更新趋势线斜率和值
|
||||
if ph {
|
||||
data.slopePh = slope
|
||||
data.upper = e.High.Get(length)
|
||||
} else {
|
||||
data.upper = data.upper - data.slopePh
|
||||
}
|
||||
|
||||
if pl {
|
||||
data.slopePl = slope
|
||||
data.lower = e.Low.Get(length)
|
||||
} else {
|
||||
data.lower = data.lower + data.slopePl
|
||||
}
|
||||
|
||||
// 保存前一个状态
|
||||
data.prevUpos = data.upos
|
||||
data.prevDnos = data.dnos
|
||||
|
||||
// 检测突破状态
|
||||
if ph {
|
||||
data.upos = 0
|
||||
} else if closePrice > data.upper-data.slopePh*float64(length) {
|
||||
data.upos = 1
|
||||
}
|
||||
|
||||
if pl {
|
||||
data.dnos = 0
|
||||
} else if closePrice < data.lower+data.slopePl*float64(length) {
|
||||
data.dnos = 1
|
||||
}
|
||||
|
||||
// 检测突破并生成交易信号
|
||||
upBreakout := data.upos > data.prevUpos
|
||||
dnBreakout := data.dnos > data.prevDnos
|
||||
|
||||
if upBreakout && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Up Breakout",
|
||||
})
|
||||
}
|
||||
|
||||
if dnBreakout && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Down Breakout",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
66
llmjob/3614.go
Normal file
66
llmjob/3614.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
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 J3614(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
length := pol.DefInt("length", 66, core.PNorm(20, 100))
|
||||
hmaRate := pol.Def("hmaRate", 8.72, core.PNorm(3, 15))
|
||||
channelFactor := pol.Def("channelFactor", 0.98, core.PNorm(0.95, 0.995))
|
||||
|
||||
// 计算HMA实际周期
|
||||
hmaPeriod := int(float64(length) * hmaRate)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: hmaPeriod, // 预热数量取最大的指标周期
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"1h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算线性回归线
|
||||
linreg := ta.LinReg(e.Close, length)
|
||||
linregValue := linreg.Get(0)
|
||||
|
||||
// 计算通道上限线
|
||||
band2 := linregValue * channelFactor
|
||||
|
||||
// 计算Hull移动平均线
|
||||
hma := ta.HMA(e.Close, hmaPeriod)
|
||||
hmaValue := hma.Get(0)
|
||||
|
||||
// 获取当前收盘价
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(closePrice) || math.IsNaN(linregValue) || math.IsNaN(band2) || math.IsNaN(hmaValue) {
|
||||
return
|
||||
}
|
||||
|
||||
// 生成交易信号
|
||||
longCondition := closePrice < band2 && hmaValue < closePrice
|
||||
shortCondition := closePrice > linregValue
|
||||
|
||||
// 执行交易操作
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
// 满足做多条件且没有多头持仓时开仓
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "BUY",
|
||||
})
|
||||
} else if shortCondition && len(s.LongOrders) > 0 {
|
||||
// 满足平仓条件且持有多头仓位时平仓
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "SELL",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
218
llmjob/3644.go
Normal file
218
llmjob/3644.go
Normal file
@@ -0,0 +1,218 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// SupertrendState 存储每个品种的策略状态
|
||||
type SupertrendState struct {
|
||||
trend int // 趋势状态,1表示上升趋势,-1表示下降趋势
|
||||
prevTrend int // 前一期的趋势状态
|
||||
up float64 // 上轨
|
||||
dn float64 // 下轨
|
||||
tp float64 // 止盈价格
|
||||
}
|
||||
|
||||
func J3644(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
atrPeriod := pol.DefInt("atrPeriod", 5, core.PNorm(2, 20))
|
||||
atrMultiplier := pol.Def("atrMultiplier", 4.7, core.PNorm(2.0, 8.0))
|
||||
profitRatio := pol.Def("profitRatio", 0.382, core.PNorm(0.2, 1.0))
|
||||
useSmaATR := pol.DefInt("useSmaATR", 1, core.PUniform(0, 1)) // 1=true, 0=false
|
||||
srcType := pol.DefInt("srcType", 0, core.PUniform(0, 7)) // 0=hlc3, 1=high, 2=low, 3=open, 4=hl2, 5=close, 6=hlcc4, 7=ohlc4
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: atrPeriod * 3, // 预热期设置为ATR周期的3倍,确保有足够数据
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多开1个多单
|
||||
EachMaxShort: 1, // 最多开1个空单
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略状态
|
||||
s.More = &SupertrendState{
|
||||
trend: 1, // 初始趋势状态为上升趋势
|
||||
prevTrend: 1, // 前一期趋势状态也为上升趋势
|
||||
tp: 0, // 初始止盈价格
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*SupertrendState)
|
||||
|
||||
// 获取数据源
|
||||
var src *ta.Series
|
||||
switch srcType {
|
||||
case 0:
|
||||
src = ta.HLC3(e.High, e.Low, e.Close)
|
||||
case 1:
|
||||
src = e.High
|
||||
case 2:
|
||||
src = e.Low
|
||||
case 3:
|
||||
src = e.Open
|
||||
case 4:
|
||||
src = ta.HL2(e.High, e.Low)
|
||||
case 5:
|
||||
src = e.Close
|
||||
case 6:
|
||||
// HLCC4: (high + low + close + close) / 4
|
||||
src = e.High.Add(e.Low).Add(e.Close).Add(e.Close).Div(4)
|
||||
case 7:
|
||||
// OHLC4: (open + high + low + close) / 4
|
||||
src = e.Open.Add(e.High).Add(e.Low).Add(e.Close).Div(4)
|
||||
default:
|
||||
src = ta.HLC3(e.High, e.Low, e.Close)
|
||||
}
|
||||
|
||||
// 计算ATR
|
||||
var atr *ta.Series
|
||||
if useSmaATR == 1 {
|
||||
// 使用标准ATR计算
|
||||
atr = ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
} else {
|
||||
// 使用SMA(TR)计算ATR
|
||||
trueRange := ta.TR(e.High, e.Low, e.Close)
|
||||
atr = ta.SMA(trueRange, atrPeriod)
|
||||
}
|
||||
|
||||
// 计算超级趋势通道
|
||||
closePrice := e.Close.Get(0)
|
||||
prevClose := e.Close.Get(1)
|
||||
atrVal := atr.Get(0)
|
||||
srcVal := src.Get(0)
|
||||
|
||||
// 计算上下轨
|
||||
up := srcVal - (atrMultiplier * atrVal)
|
||||
dn := srcVal + (atrMultiplier * atrVal)
|
||||
|
||||
// 获取前一期上下轨
|
||||
prevUp := state.up
|
||||
prevDn := state.dn
|
||||
|
||||
// 如果是第一期或前一期值无效,使用当前值
|
||||
if math.IsNaN(prevUp) {
|
||||
prevUp = up
|
||||
}
|
||||
if math.IsNaN(prevDn) {
|
||||
prevDn = dn
|
||||
}
|
||||
|
||||
// 趋势跟踪逻辑
|
||||
if !math.IsNaN(prevClose) && prevClose > prevUp {
|
||||
up = math.Max(up, prevUp)
|
||||
}
|
||||
if !math.IsNaN(prevClose) && prevClose < prevDn {
|
||||
dn = math.Min(dn, prevDn)
|
||||
}
|
||||
|
||||
// 更新趋势状态
|
||||
state.prevTrend = state.trend
|
||||
if state.trend == -1 && closePrice > prevDn {
|
||||
state.trend = 1
|
||||
} else if state.trend == 1 && closePrice < prevUp {
|
||||
state.trend = -1
|
||||
}
|
||||
|
||||
// 保存当前上下轨值
|
||||
state.up = up
|
||||
state.dn = dn
|
||||
|
||||
// 生成交易信号
|
||||
buySignal := state.trend == 1 && state.prevTrend == -1
|
||||
sellSignal := state.trend == -1 && state.prevTrend == 1
|
||||
|
||||
// 获取当前订单
|
||||
longOrders := s.LongOrders
|
||||
shortOrders := s.ShortOrders
|
||||
|
||||
// 更新多头订单的止损
|
||||
if len(longOrders) > 0 {
|
||||
for _, od := range longOrders {
|
||||
if od.Status >= ormo.InOutStatusFullEnter {
|
||||
// 保持之前的止盈价
|
||||
tp := state.tp
|
||||
if math.IsNaN(tp) || tp <= 0 {
|
||||
tp = od.InitPrice + (od.InitPrice-up)*profitRatio
|
||||
}
|
||||
// 更新止损价为上轨
|
||||
od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: up,
|
||||
Tag: "super_trend_sl",
|
||||
})
|
||||
// 更新止盈价
|
||||
od.SetTakeProfit(&ormo.ExitTrigger{
|
||||
Price: tp,
|
||||
Tag: "super_trend_tp",
|
||||
})
|
||||
state.tp = tp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新空头订单的止损
|
||||
if len(shortOrders) > 0 {
|
||||
for _, od := range shortOrders {
|
||||
if od.Status >= ormo.InOutStatusFullEnter {
|
||||
// 保持之前的止盈价
|
||||
tp := state.tp
|
||||
if math.IsNaN(tp) || tp <= 0 {
|
||||
tp = od.InitPrice - (dn-od.InitPrice)*profitRatio
|
||||
}
|
||||
// 更新止损价为下轨
|
||||
od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: dn,
|
||||
Tag: "super_trend_sl",
|
||||
})
|
||||
// 更新止盈价
|
||||
od.SetTakeProfit(&ormo.ExitTrigger{
|
||||
Price: tp,
|
||||
Tag: "super_trend_tp",
|
||||
})
|
||||
state.tp = tp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 买入信号
|
||||
if buySignal && len(longOrders) == 0 {
|
||||
// 计算止盈价
|
||||
tp := closePrice + (closePrice-up)*profitRatio
|
||||
state.tp = tp
|
||||
// 开多单,使用限价单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "super_trend_long",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeLimit,
|
||||
Limit: tp,
|
||||
StopLoss: up,
|
||||
StopLossTag: "super_trend_sl",
|
||||
TakeProfit: tp,
|
||||
TakeProfitTag: "super_trend_tp",
|
||||
})
|
||||
}
|
||||
|
||||
// 卖出信号
|
||||
if sellSignal && len(shortOrders) == 0 {
|
||||
// 计算止盈价
|
||||
tp := closePrice - (dn-closePrice)*profitRatio
|
||||
state.tp = tp
|
||||
// 开空单,使用限价单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "super_trend_short",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeLimit,
|
||||
Limit: tp,
|
||||
StopLoss: dn,
|
||||
StopLossTag: "super_trend_sl",
|
||||
TakeProfit: tp,
|
||||
TakeProfitTag: "super_trend_tp",
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
86
llmjob/3683.go
Normal file
86
llmjob/3683.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
type MacdState struct {
|
||||
lastCrossUp bool
|
||||
lastCrossDown bool
|
||||
}
|
||||
|
||||
func J3683(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
fastPeriod := pol.DefInt("fastPeriod", 16, core.PNorm(5, 20))
|
||||
emaRatio := pol.Def("emaRatio", 1.7, core.PNorm(1.5, 4))
|
||||
signalPeriod := pol.DefInt("signalPeriod", 8, core.PNorm(5, 15))
|
||||
profitRate := pol.Def("profitRate", 0.012, core.PNorm(0.005, 0.03))
|
||||
lossRate := pol.Def("lossRate", 0.009, core.PNorm(0.005, 0.03))
|
||||
|
||||
// 计算慢速EMA周期
|
||||
slowPeriod := int(float64(fastPeriod) * emaRatio)
|
||||
|
||||
// 确保预热K线数量足够计算MACD指标
|
||||
warmupNum := fastPeriod + slowPeriod + signalPeriod
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的状态变量
|
||||
s.More = &MacdState{
|
||||
lastCrossUp: false,
|
||||
lastCrossDown: false,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*MacdState)
|
||||
|
||||
// 计算MACD指标
|
||||
macdLine, signalLine := ta.MACD(e.Close, fastPeriod, slowPeriod, signalPeriod)
|
||||
|
||||
// 检测金叉和死叉
|
||||
crossUp := macdLine.Cross(signalLine)
|
||||
crossDown := signalLine.Cross(macdLine)
|
||||
|
||||
// 更新交叉状态
|
||||
if crossUp == 1 { // 金叉
|
||||
state.lastCrossUp = true
|
||||
state.lastCrossDown = false
|
||||
} else if crossDown == 1 { // 死叉
|
||||
state.lastCrossUp = false
|
||||
state.lastCrossDown = true
|
||||
}
|
||||
|
||||
// 获取当前价格
|
||||
currentPrice := e.Close.Get(0)
|
||||
|
||||
// 连续金叉且当前无多头订单,开多仓
|
||||
if state.lastCrossUp && crossUp == 1 && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "买入开多",
|
||||
TakeProfitVal: currentPrice * (1 + profitRate),
|
||||
StopLossVal: currentPrice * (1 - lossRate),
|
||||
TakeProfitTag: "多单止盈",
|
||||
StopLossTag: "多单止损",
|
||||
})
|
||||
}
|
||||
|
||||
// 连续死叉且当前无空头订单,开空仓
|
||||
if state.lastCrossDown && crossDown == 1 && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "卖出开空",
|
||||
Short: true,
|
||||
TakeProfitVal: currentPrice * (1 - profitRate),
|
||||
StopLossVal: currentPrice * (1 + lossRate),
|
||||
TakeProfitTag: "空单止盈",
|
||||
StopLossTag: "空单止损",
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
72
llmjob/3707.go
Normal file
72
llmjob/3707.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
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 J3707(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义策略参数
|
||||
rangeLength := pol.DefInt("rangeLength", 5, core.PNorm(3, 20))
|
||||
useHeikinAshi := pol.Def("useHeikinAshi", 0.0, core.PUniform(0, 1)) // 1.0表示true,0.0表示false
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: rangeLength + 1, // 确保有足够的历史数据计算指标
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"3d"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 获取K线数据
|
||||
var open, close *ta.Series
|
||||
if useHeikinAshi > 0.5 {
|
||||
// 使用Heikin Ashi蜡烛图
|
||||
haOpen, _, _, haClose := ta.HeikinAshi(e)
|
||||
open = haOpen
|
||||
close = haClose
|
||||
} else {
|
||||
// 使用标准K线
|
||||
open = e.Open
|
||||
close = e.Close
|
||||
}
|
||||
|
||||
// 计算成交量范围
|
||||
highest := ta.Highest(e.Volume, rangeLength)
|
||||
_ = ta.Lowest(e.Volume.Mul(-1), rangeLength) // 最低负成交量
|
||||
|
||||
// 获取当前值和前一个值
|
||||
highestCurr := highest.Get(0)
|
||||
highestPrev := highest.Get(1)
|
||||
closeCurr := close.Get(0)
|
||||
openCurr := open.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(highestCurr) || math.IsNaN(highestPrev) || math.IsNaN(closeCurr) || math.IsNaN(openCurr) {
|
||||
return
|
||||
}
|
||||
|
||||
// 生成交易信号
|
||||
volumeBreakout := highestCurr > highestPrev
|
||||
isBullish := closeCurr > openCurr
|
||||
isBearish := closeCurr < openCurr
|
||||
|
||||
// 执行交易
|
||||
if volumeBreakout && isBullish {
|
||||
// 成交量突破前高且为阳线,做多
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long Entry",
|
||||
})
|
||||
} else if volumeBreakout && isBearish {
|
||||
// 成交量突破前高且为阴线,做空
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short Entry",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
163
llmjob/3746.go
Normal file
163
llmjob/3746.go
Normal file
@@ -0,0 +1,163 @@
|
||||
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"
|
||||
)
|
||||
|
||||
type KijunData struct {
|
||||
initialCapital float64
|
||||
netProfit float64
|
||||
}
|
||||
|
||||
func J3746(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
ksPeriod := pol.DefInt("ksPeriod", 30, core.PNorm(9, 50))
|
||||
atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 30))
|
||||
atrMultiplier := pol.Def("atrMultiplier", 1.5, core.PNorm(0.8, 4))
|
||||
targetPoints := pol.Def("targetPoints", 100, core.PNorm(50, 200))
|
||||
riskPercent := pol.Def("riskPercent", 4, core.PNorm(1, 10))
|
||||
equityProtection := pol.Def("equityProtection", 30, core.PNorm(10, 50))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: ksPeriod, // 预热K线数量,确保基均线计算有足够数据
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"3d"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的特定数据
|
||||
s.More = &KijunData{
|
||||
initialCapital: 50000.0, // 初始资金
|
||||
netProfit: 0.0, // 初始净利润
|
||||
}
|
||||
},
|
||||
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
m, _ := s.More.(*KijunData)
|
||||
|
||||
// 计算技术指标
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
atrValue := atr.Get(0)
|
||||
|
||||
// 计算基均线 (Kijun Sen)
|
||||
highest := ta.Highest(e.High, ksPeriod)
|
||||
lowest := ta.Lowest(e.Low, ksPeriod)
|
||||
kijunSen := highest.Add(lowest).Div(2)
|
||||
kijunSenValue := kijunSen.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(atrValue) || math.IsNaN(kijunSenValue) {
|
||||
return
|
||||
}
|
||||
|
||||
// 生成交易信号
|
||||
openPrice := e.Open.Get(0)
|
||||
closePrice := e.Close.Get(0)
|
||||
baseLong := openPrice < kijunSenValue && closePrice > kijunSenValue
|
||||
baseShort := openPrice > kijunSenValue && closePrice < kijunSenValue
|
||||
|
||||
// 计算止损距离
|
||||
stopLossVal := atrValue * atrMultiplier
|
||||
|
||||
// 更新净利润
|
||||
// 计算总浮动盈亏
|
||||
totalFloating := 0.0
|
||||
for _, od := range s.LongOrders {
|
||||
totalFloating += od.Profit
|
||||
}
|
||||
for _, od := range s.ShortOrders {
|
||||
totalFloating += od.Profit
|
||||
}
|
||||
m.netProfit = totalFloating
|
||||
|
||||
// 权益保护检查
|
||||
equityStopout := false
|
||||
if s.OrderNum > 0 {
|
||||
// 计算当前余额(初始资金 + 净利润)
|
||||
totalBalance := m.initialCapital + m.netProfit
|
||||
|
||||
// 检查是否触发权益保护
|
||||
if totalFloating < 0 && math.Abs(totalFloating/totalBalance) > equityProtection/100 {
|
||||
equityStopout = true
|
||||
}
|
||||
}
|
||||
|
||||
// 权益保护触发时平仓所有头寸
|
||||
if equityStopout {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "equity_protection",
|
||||
Dirt: core.OdDirtBoth,
|
||||
Force: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有持仓
|
||||
hasLongPosition := len(s.LongOrders) > 0
|
||||
hasShortPosition := len(s.ShortOrders) > 0
|
||||
|
||||
// 计算头寸大小
|
||||
balance := m.initialCapital + m.netProfit
|
||||
riskAmount := balance * (riskPercent / 100) // 风险金额
|
||||
size := riskAmount / stopLossVal // 合约数量
|
||||
if size < 0.001 { // 最小合约数量检查
|
||||
size = 0.001
|
||||
}
|
||||
|
||||
// 多头交易
|
||||
if baseLong && !hasLongPosition && !hasShortPosition {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "l_en",
|
||||
Amount: size,
|
||||
StopLossVal: stopLossVal,
|
||||
TakeProfitVal: targetPoints,
|
||||
StopLossTag: "stop_loss",
|
||||
TakeProfitTag: "take_profit",
|
||||
})
|
||||
}
|
||||
|
||||
// 空头交易
|
||||
if baseShort && !hasShortPosition && !hasLongPosition {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "s_en",
|
||||
Short: true,
|
||||
Amount: size,
|
||||
StopLossVal: stopLossVal,
|
||||
TakeProfitVal: targetPoints,
|
||||
StopLossTag: "stop_loss",
|
||||
TakeProfitTag: "take_profit",
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
|
||||
// 计算基均线
|
||||
highest := ta.Highest(s.Env.High, ksPeriod)
|
||||
lowest := ta.Lowest(s.Env.Low, ksPeriod)
|
||||
kijunSen := highest.Add(lowest).Div(2)
|
||||
kijunSenValue := kijunSen.Get(0)
|
||||
|
||||
closePrice := s.Env.Close.Get(0)
|
||||
|
||||
// 多头平仓条件:收盘价低于基均线
|
||||
if !od.Short && closePrice < kijunSenValue {
|
||||
return &strat.ExitReq{
|
||||
Tag: "long_exit",
|
||||
}
|
||||
}
|
||||
|
||||
// 空头平仓条件:收盘价高于基均线
|
||||
if od.Short && closePrice > kijunSenValue {
|
||||
return &strat.ExitReq{
|
||||
Tag: "short_exit",
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
270
llmjob/3762.go
Normal file
270
llmjob/3762.go
Normal file
@@ -0,0 +1,270 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm/ormo"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banexg/log"
|
||||
"github.com/banbox/banexg/utils"
|
||||
ta "github.com/banbox/banta"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
/*
|
||||
超跌反弹策略。30个品种为宜;已增加暴跌抄底机制。
|
||||
开单越多,后续抄底价指数下移
|
||||
做空部分没有利润,不稳定,小亏,禁用
|
||||
*/
|
||||
|
||||
type State3762 struct {
|
||||
NoLongUntil int64
|
||||
BWidth float64
|
||||
MidPrice float64
|
||||
SlRange float64
|
||||
volRate float64
|
||||
}
|
||||
|
||||
func J3762(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义策略参数
|
||||
p := pol.DefInt("p", 20, core.PNorm(5, 50))
|
||||
d := pol.Def("d", 30.0, core.PNorm(5, 50))
|
||||
mult := pol.Def("mult", 1.5, core.PNorm(1.0, 3.0))
|
||||
slRate := pol.Def("slRate", 25, core.PNorm(0, 250)) //default: 25
|
||||
// 某品种入场时,下移其他品种的倍率(相对BWidth)
|
||||
// 0.3开单太多亏损,0.5开单不足亏损
|
||||
downOtherRate := 0.33
|
||||
|
||||
dayMS := int64(utils.TFToSecs("1d") * 1000)
|
||||
longFillNum := float64(0)
|
||||
fillLock := sync.Mutex{}
|
||||
var allJobs []*strat.StratJob
|
||||
|
||||
// 基于成交量计算的区间倍率,成交量太大时,适当调低做多和止损价格
|
||||
var rangeVolRate = float64(1)
|
||||
allowPairs := 1 // 允许挂单的订单数,默认为品种数,随开单减少,防止交易所故障导致下单过多
|
||||
openPairs := 0 // 实际有订单的品种数(挂单+持仓)
|
||||
|
||||
lenAtr := 20
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
|
||||
calcBuyPrice := func(m *State3762) float64 {
|
||||
return m.MidPrice - m.BWidth*(1+math.Pow(longFillNum, 1.5)*downOtherRate)*rangeVolRate
|
||||
}
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: baseAtrLen * 2, // 预热K线数量,确保有足够数据计算指标
|
||||
RunTimeFrames: []string{"15m"},
|
||||
BatchInOut: true,
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &State3762{}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
m, _ := s.More.(*State3762)
|
||||
// 计算基础布林带指标
|
||||
sma := ta.SMA(e.Close, p)
|
||||
ma7 := ta.SMA(e.Close, 7).Get(0)
|
||||
ma30 := ta.SMA(e.Close, 30).Get(0)
|
||||
multCur := mult
|
||||
if ma7 < ma30 {
|
||||
// 熊市下移下限
|
||||
multCur *= 1.35
|
||||
}
|
||||
stddev := ta.StdDev(e.Close, p).Get(0) * multCur
|
||||
upperBB := sma.Get(0) + stddev
|
||||
lowerBB := sma.Get(0) - stddev
|
||||
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
|
||||
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
|
||||
|
||||
maVol := ta.EMA(e.Volume, lenAtr)
|
||||
volBase := ta.Lowest(ta.Highest(maVol, lenAtr), baseAtrLen).Get(0)
|
||||
m.volRate = maVol.Get(0) / volBase
|
||||
volH3Rate := ta.Highest(maVol, 3).Get(0) / volBase
|
||||
|
||||
// 计算百分比偏离轨道
|
||||
atrRt := d
|
||||
if ma7 < ma30 {
|
||||
// 熊市下移下限
|
||||
atrRt *= 1.35
|
||||
}
|
||||
upex2 := sma.Get(0) + atrBase*atrRt
|
||||
dnex2 := sma.Get(0) - atrBase*atrRt
|
||||
|
||||
// 计算最终轨道
|
||||
upexPrice := max(upperBB, (upex2+upperBB)/2)
|
||||
dnexPrice := min(lowerBB, (dnex2+lowerBB)/2)
|
||||
m.BWidth = (upexPrice - dnexPrice) / 2
|
||||
m.MidPrice = (upexPrice + dnexPrice) / 2
|
||||
//lowPrice, highPrice := dnexPrice, upexPrice
|
||||
lowPrice := calcBuyPrice(m)
|
||||
|
||||
// 获取当前价格
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 平仓条件:阳线
|
||||
if len(s.LongOrders) > 0 && s.LongOrders[0].Status >= ormo.InOutStatusPartEnter {
|
||||
doExit := closePrice > e.Open.Get(0)
|
||||
if volH3Rate > 5 {
|
||||
// 放巨量入场的订单,等待阳线后一个K线出场
|
||||
doExit = e.Close.Get(1) > e.Open.Get(1)
|
||||
}
|
||||
if doExit {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "ExitLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
if math.IsNaN(lowPrice) || lowPrice <= 0 {
|
||||
return
|
||||
}
|
||||
slRange := min(atrBase*slRate, closePrice*0.25) * min(3, rangeVolRate)
|
||||
m.SlRange = slRange
|
||||
|
||||
// 执行交易逻辑
|
||||
//if !s.IsWarmUp && e.TimeStop >= 1760130900000 {
|
||||
// line := fmt.Sprintf("%s upp: %.6f, low: %.6f upex2: %.6f, dnex2: %.6f dnex: %.6f slRange: %.6f",
|
||||
// e.Symbol, upperBB, lowerBB, upex2, dnex2, lowPrice, slRange)
|
||||
// s.Strat.WriteOutput(line, true)
|
||||
//}
|
||||
if len(s.LongOrders) == 0 {
|
||||
// 使用限价单在下轨价格做多,不依赖当前价格是否突破轨道
|
||||
if e.TimeStop >= m.NoLongUntil && openPairs < allowPairs {
|
||||
fillLock.Lock()
|
||||
openPairs += 1
|
||||
fillLock.Unlock()
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Limit: lowPrice, // 以下轨价格作为限价单
|
||||
StopLossVal: slRange,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for _, od := range s.LongOrders {
|
||||
if od.Status == ormo.InOutStatusInit {
|
||||
err := od.SetEnterLimit(lowPrice)
|
||||
if err != nil {
|
||||
log.Error("SetEnterLimit fail", zap.Error(err))
|
||||
} else {
|
||||
err = od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: lowPrice - slRange,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("SetStopLoss fail", zap.Error(err))
|
||||
}
|
||||
}
|
||||
} else if od.Status == ormo.InOutStatusFullEnter {
|
||||
barNum := e.BarCount(od.Enter.UpdateAt)
|
||||
if barNum > 2 && od.ProfitRate < 0 {
|
||||
// 持仓2个k线,出现亏损,平仓
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "ExitBad",
|
||||
Dirt: core.OdDirtLong,
|
||||
Log: true,
|
||||
})
|
||||
m.NoLongUntil = e.TimeStop + dayMS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
fillLock.Lock()
|
||||
longFillNum = 0
|
||||
openPairs = 0
|
||||
volRates := make([]float64, 0, len(jobs))
|
||||
for _, s := range jobs {
|
||||
if len(s.LongOrders) > 0 {
|
||||
openPairs += 1
|
||||
for _, od := range s.LongOrders {
|
||||
if od.Status >= ormo.InOutStatusPartEnter {
|
||||
longFillNum += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
m, _ := s.More.(*State3762)
|
||||
volRates = append(volRates, m.volRate)
|
||||
}
|
||||
fillLock.Unlock()
|
||||
allJobs = jobs
|
||||
sort.Float64s(volRates)
|
||||
rangeVolRate = max(1, math.Pow(volRates[len(volRates)/2]/2, 0.3))
|
||||
allowPairs = int(math.Round(math.Pow(0.8, longFillNum) * float64(len(allJobs))))
|
||||
//jobs[0].Strat.WriteOutput(fmt.Sprintf("%.3f", rangeVolRate), true)
|
||||
},
|
||||
OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) {
|
||||
if chgType == core.OdChgExitFill {
|
||||
if od.Enter.Amount > 0 {
|
||||
m, _ := s.More.(*State3762)
|
||||
m.NoLongUntil = od.ExitAt + dayMS
|
||||
fillLock.Lock()
|
||||
longFillNum -= 1
|
||||
openPairs -= 1
|
||||
fillLock.Unlock()
|
||||
}
|
||||
} else if chgType == core.OdChgEnterFill {
|
||||
fillLock.Lock()
|
||||
longFillNum += 1
|
||||
openPairs = 0 // 这里应当重新计算,避免并发和回调导致错误
|
||||
for _, job := range allJobs {
|
||||
if len(job.LongOrders) > 0 {
|
||||
openPairs += 1
|
||||
}
|
||||
}
|
||||
fillLock.Unlock()
|
||||
// 降低允许开仓的品种数,避免交易所故障导致下单太多
|
||||
allowPairs = int(math.Round(math.Pow(0.85, longFillNum) * float64(len(allJobs))))
|
||||
needClose := openPairs - allowPairs // 需要关闭的订单数
|
||||
if needClose > 0 {
|
||||
// 排序确保多次回测一致
|
||||
sort.Slice(allJobs, func(i, j int) bool {
|
||||
return allJobs[i].Symbol.Symbol < allJobs[j].Symbol.Symbol
|
||||
})
|
||||
}
|
||||
odMgr := biz.GetOdMgr(s.Account)
|
||||
// 移动其他品种的入场价格,避免同时开仓太多
|
||||
for _, job := range allJobs {
|
||||
if job == s || len(job.LongOrders) == 0 {
|
||||
continue
|
||||
}
|
||||
m, _ := job.More.(*State3762)
|
||||
lowPrice := calcBuyPrice(m)
|
||||
for _, odr := range job.LongOrders {
|
||||
if odr.Status <= ormo.InOutStatusPartEnter {
|
||||
if lowPrice <= 0 || needClose > 0 {
|
||||
needClose -= 1
|
||||
job.CloseOrders(&strat.ExitReq{
|
||||
Tag: "cancel",
|
||||
OrderID: odr.ID,
|
||||
Force: true,
|
||||
})
|
||||
odMgr.ProcessOrders(job)
|
||||
} else {
|
||||
err := odr.SetEnterLimit(lowPrice)
|
||||
if err == nil {
|
||||
odr.EnterTag = fmt.Sprintf("long%d", int(longFillNum))
|
||||
odr.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: max(lowPrice-m.SlRange, lowPrice*0.1),
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
odr.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: max(lowPrice-m.SlRange, odr.Enter.Price*0.1),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
310
llmjob/3782.go
Normal file
310
llmjob/3782.go
Normal file
@@ -0,0 +1,310 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
/*
|
||||
需要多品种综合市场信息,品种数不可少于30
|
||||
此策略有综合市场信息并限制开单数量,回测和实盘开单预期不一致,但开单数量基本符合
|
||||
openMaxs中的前两个减少会降低收益
|
||||
|
||||
待改进:
|
||||
zone1信号在22年有几次略大回撤
|
||||
*/
|
||||
|
||||
// ZoneTradeState 用于存储每个交易区间的状态信息
|
||||
type ZoneTradeState struct {
|
||||
LastTimes [3]int64 // 3个买入信号的最后交易时间
|
||||
LastSellTime int64 // 最后卖出时间
|
||||
atrBase float64
|
||||
score float64
|
||||
upFlag float64
|
||||
volRate float64
|
||||
}
|
||||
|
||||
func J3782(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
maPeriod := pol.DefInt("maPeriod", 100, core.PNorm(20, 200))
|
||||
baseRetra := pol.Def("baseRetra", 0.08, core.PNorm(0.05, 0.2))
|
||||
retraRate := pol.Def("retraRate", 1.8, core.PNorm(1.5, 2.5))
|
||||
profitRate := pol.Def("profitRate", 0.03, core.PNorm(0.015, 0.06))
|
||||
tradeDelay := pol.Def("tradeDelay", 40, core.PNorm(10, 100))
|
||||
slRate := pol.Def("slRate", 63, core.PNorm(5, 35))
|
||||
openMaxs := [3]int{2, 8, 13} // 同一个bar最大同时允许开首单的数量
|
||||
holdMaxs := [3]int{25, 35, 50} // 最大同时持仓数
|
||||
buyWeis := [3]float64{1, 1.5, 3} // 不同信号开仓倍率
|
||||
maxJobTrades := [3]int{1, 3, 4} // 单个品种最大开仓数量
|
||||
|
||||
// 计算各回撤比例
|
||||
retracement2 := baseRetra
|
||||
retracement3 := baseRetra * retraRate
|
||||
retracement4 := baseRetra * retraRate * 1.33
|
||||
|
||||
lenAtr := 20
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
rates := make([]float64, 0, 10)
|
||||
|
||||
allOpens := 0
|
||||
openNums := [3]int{0, 0, 0} // 同一个bar开单数量
|
||||
holdNums := [3]int{0, 0, 0} // 总计持仓数量
|
||||
openUnits := 0.0
|
||||
var pairLock sync.Mutex
|
||||
addThres, firstRate := 1.0, 1.0
|
||||
avgAtrBase := 2.5
|
||||
maxAllowUnit := 3.0 // 这里默认为品种数
|
||||
rangeVolRate := 1.0 // 全局成交量倍率
|
||||
|
||||
checkMaxPair := func(level int, openNum int) (int, float64) {
|
||||
if openNum >= maxJobTrades[level] {
|
||||
return -1, 0
|
||||
}
|
||||
costRate := buyWeis[level]
|
||||
pairLock.Lock()
|
||||
if openUnits < maxAllowUnit && openNums[level] < openMaxs[level] && holdNums[level] < holdMaxs[level] {
|
||||
openNums[level] += 1
|
||||
holdNums[level] += 1
|
||||
allOpens += 1
|
||||
openUnits += costRate
|
||||
} else {
|
||||
level = -1
|
||||
}
|
||||
pairLock.Unlock()
|
||||
return level, costRate
|
||||
}
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: maPeriod,
|
||||
BatchInfo: true,
|
||||
BatchInOut: true,
|
||||
RunTimeFrames: []string{"15m"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化状态信息
|
||||
s.More = &ZoneTradeState{
|
||||
LastTimes: [3]int64{},
|
||||
}
|
||||
},
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
return []*strat.PairSub{
|
||||
{"_cur_", "4h", baseAtrLen * 2},
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*ZoneTradeState)
|
||||
|
||||
// 计算MA100
|
||||
ma100 := ta.SMA(e.Close, maPeriod)
|
||||
ma100Val := ma100.Get(0)
|
||||
|
||||
// 计算交易价格水平
|
||||
curRate := max(addThres, 1+state.score/10)
|
||||
itemRate := max(0.7, min(math.Sqrt(state.atrBase/avgAtrBase), 1.4))
|
||||
buyPrice2 := ma100Val * (1 - retracement2*curRate*itemRate*firstRate)
|
||||
buyPrice3 := ma100Val * (1 - retracement3*curRate*itemRate)
|
||||
buyPrice4 := ma100Val * (1 - retracement4*curRate*itemRate)
|
||||
sellPrice := ma100Val * (1 + profitRate)
|
||||
gapRange := (buyPrice2 - buyPrice4) / 2
|
||||
|
||||
maVol := ta.EMA(e.Volume, lenAtr)
|
||||
volBase := ta.Lowest(ta.Highest(maVol, lenAtr), baseAtrLen).Get(0)
|
||||
state.volRate = maVol.Get(0) / volBase
|
||||
|
||||
// 获取当前价格
|
||||
lowPrice := e.Low.Get(0)
|
||||
highPrice := e.High.Get(0)
|
||||
|
||||
// 检查交易间隔条件
|
||||
currentBarTime := e.TimeStop
|
||||
|
||||
lastEnt := max(state.LastTimes[0], state.LastTimes[1], state.LastTimes[2])
|
||||
canTrade := e.BarCount(lastEnt) >= 10
|
||||
canTrade2 := canTrade && e.BarCount(state.LastTimes[0]) >= tradeDelay
|
||||
canTrade3 := canTrade && e.BarCount(state.LastTimes[1]) >= tradeDelay
|
||||
canTrade4 := canTrade && e.BarCount(state.LastTimes[2]) >= tradeDelay
|
||||
canSell := e.BarCount(state.LastSellTime) >= tradeDelay
|
||||
|
||||
// 获取当前总开仓数量,与原始策略的strategy.opentrades保持一致
|
||||
openTrades := len(s.LongOrders) + len(s.ShortOrders)
|
||||
|
||||
buyIdx, costRate := -1, 0.0
|
||||
if lowPrice <= buyPrice4 && canTrade4 {
|
||||
buyIdx, costRate = checkMaxPair(2, openTrades)
|
||||
} else if lowPrice <= buyPrice3 && canTrade3 {
|
||||
buyIdx, costRate = checkMaxPair(1, openTrades)
|
||||
} else if lowPrice <= buyPrice2 && canTrade2 {
|
||||
buyIdx, costRate = checkMaxPair(0, openTrades)
|
||||
}
|
||||
if buyIdx >= 0 && costRate > 0 {
|
||||
callNum := allOpens
|
||||
//if buyIdx == 0 {
|
||||
// // 对信号1,累计开单较多时,下移入场价
|
||||
// pairLock.Lock()
|
||||
// callNum = max(callNum, int(math.Round(float64(holdNums[0])*0.5))-3)
|
||||
// pairLock.Unlock()
|
||||
//}
|
||||
// 同一个bar开单多次,从第三次开始,使用ln(num)下移入场价
|
||||
currRate := 1 + max(0, math.Log10(float64(callNum-2)))
|
||||
// 用倒抛物线映射成交量倍率,作为入场价距离调整系数
|
||||
volRate := 1 + max(0, 0.4-math.Pow(1.4*(rangeVolRate-1.5), 2))
|
||||
rangeRate := math.Pow(volRate*currRate, 0.3) - 1
|
||||
limitPrice := float64(0)
|
||||
if (rangeVolRate > 1.2 || callNum > 2) && rangeRate > 0.01 {
|
||||
limitPrice = e.Close.Get(0) - rangeRate*gapRange
|
||||
}
|
||||
tag := fmt.Sprintf("zone%d", buyIdx+1)
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: tag,
|
||||
CostRate: costRate,
|
||||
Limit: limitPrice,
|
||||
Log: true,
|
||||
StopBars: 3,
|
||||
})
|
||||
state.LastTimes[buyIdx] = currentBarTime
|
||||
}
|
||||
|
||||
// 检查卖出条件
|
||||
if s.OrderNum > 0 {
|
||||
curPrice := e.Close.Get(0)
|
||||
slRange := slRate * itemRate * curPrice / 100
|
||||
avgPrice := s.PositionAvgPrice(core.OdDirtLong, "")
|
||||
if curPrice+slRange <= avgPrice {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "StopLoss",
|
||||
Log: true,
|
||||
})
|
||||
state.LastSellTime = currentBarTime
|
||||
} else {
|
||||
exitPrice := avgPrice*(1+profitRate)*0.4 + sellPrice*0.6
|
||||
if openTrades > 0 && highPrice >= exitPrice && canSell {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "SellAll",
|
||||
Log: true,
|
||||
})
|
||||
state.LastSellTime = currentBarTime
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
|
||||
// 多指标综合计算大周期行情下跌分数
|
||||
score := float64(0)
|
||||
if ta.RSI(e.Close, 14).Get(0) < 30 {
|
||||
score += 1
|
||||
}
|
||||
roc := ta.ROC(e.Close, 20).Get(0)
|
||||
if roc < -20 {
|
||||
score += 2
|
||||
} else if roc < -10 {
|
||||
score += 1
|
||||
}
|
||||
plusDi, minusDi := ta.PluMinDI(e.High, e.Low, e.Close, 14)
|
||||
adx := ta.ADX(e.High, e.Low, e.Close, 14).Get(0)
|
||||
if minusDi.Get(0) > plusDi.Get(0) {
|
||||
if adx > 40 {
|
||||
score += 2
|
||||
} else if adx > 25 {
|
||||
score += 1
|
||||
}
|
||||
}
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
|
||||
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
|
||||
rates = append(rates, atrBase/(e.Close.Get(0)*0.01))
|
||||
//openRate := 0.006 * closePrice / atrBase
|
||||
//if openRate < 1 {
|
||||
// openRate = math.Sqrt(openRate)
|
||||
//}
|
||||
//openRate = max(0.7, min(1.5, openRate))
|
||||
ma7 := ta.SMA(e.Close, 7).Get(0)
|
||||
ma20 := ta.SMA(e.Close, 20)
|
||||
ma20V := ma20.Get(0)
|
||||
ma99 := ta.SMA(e.Close, 99).Get(0)
|
||||
if ma99 > ma20V {
|
||||
score += 1
|
||||
}
|
||||
state, _ := s.More.(*ZoneTradeState)
|
||||
state.score = score
|
||||
state.upFlag = 0
|
||||
state.atrBase = atrBase / (e.Close.Get(0) * 0.01)
|
||||
if ma20V > ma99 && ma20V > ma20.Get(2) {
|
||||
state.upFlag = 1
|
||||
} else if ma7 < min(ma20V, ma99) {
|
||||
state.upFlag = -1
|
||||
}
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
pairLock.Lock()
|
||||
openUnits = 0
|
||||
allOpens = 0
|
||||
openNums = [3]int{0, 0, 0}
|
||||
holdNums = [3]int{0, 0, 0}
|
||||
volRates := make([]float64, 0, len(jobs))
|
||||
for _, j := range jobs {
|
||||
for _, od := range j.LongOrders {
|
||||
if od.EnterTag == "zone1" {
|
||||
holdNums[0] += 1
|
||||
} else if od.EnterTag == "zone2" {
|
||||
holdNums[1] += 1
|
||||
} else if od.EnterTag == "zone3" {
|
||||
holdNums[2] += 1
|
||||
}
|
||||
}
|
||||
m, _ := j.More.(*ZoneTradeState)
|
||||
volRates = append(volRates, m.volRate)
|
||||
}
|
||||
pairLock.Unlock()
|
||||
sort.Float64s(volRates)
|
||||
rangeVolRate = max(1, math.Pow(volRates[len(volRates)/2]/2, 0.3))
|
||||
},
|
||||
OnBatchInfos: func(tf string, jobs map[string]*strat.JobEnv) {
|
||||
if len(jobs) < 3 {
|
||||
return
|
||||
}
|
||||
// 多品种综合计算整体市场下行激烈程度
|
||||
downNum, downHeavyNum := float64(0), float64(0)
|
||||
atrRates := make([]float64, 0, len(jobs))
|
||||
for _, j := range jobs {
|
||||
state, _ := j.Job.More.(*ZoneTradeState)
|
||||
if state.score >= 5 {
|
||||
downHeavyNum += 1
|
||||
}
|
||||
if state.score >= 3 {
|
||||
downNum += 1
|
||||
}
|
||||
atrRates = append(atrRates, state.atrBase)
|
||||
}
|
||||
jobNum := float64(len(jobs))
|
||||
downRate := downNum / jobNum
|
||||
downHeavyRate := downHeavyNum / jobNum
|
||||
sort.Float64s(atrRates)
|
||||
avgAtrBase = atrRates[len(jobs)/2]
|
||||
if downHeavyRate >= 0.8 {
|
||||
// 极端下跌
|
||||
addThres, firstRate = 1.6, 1.2
|
||||
maxAllowUnit = jobNum * 0.5
|
||||
} else if downRate >= 0.85 && downHeavyRate >= 0.6 {
|
||||
// 严重下跌
|
||||
addThres, firstRate = 1.45, 1.15
|
||||
maxAllowUnit = jobNum * 0.55
|
||||
} else if downRate >= 0.7 && downRate <= 0.85 {
|
||||
// 市场中度下跌
|
||||
addThres, firstRate = 1.3, 1.1
|
||||
maxAllowUnit = jobNum * 0.6
|
||||
} else if downRate >= 0.5 && downRate <= 0.7 {
|
||||
// 轻度下跌
|
||||
addThres, firstRate = 1.15, 1.05
|
||||
maxAllowUnit = jobNum * 0.7
|
||||
} else {
|
||||
// 轻度或未下跌
|
||||
maxAllowUnit = jobNum
|
||||
addThres, firstRate = 1, 1
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
144
llmjob/3827.go
Normal file
144
llmjob/3827.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
// FibRSIGoldenCross 用于存储每个品种的斐波那契回撤水平
|
||||
type FibRSIGoldenCross struct {
|
||||
FibHigh float64
|
||||
FibLow float64
|
||||
Fib38 float64
|
||||
Fib50 float64
|
||||
Fib61 float64
|
||||
}
|
||||
|
||||
func J3827(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
short_ma_length := pol.DefInt("short_ma_length", 40, core.PNorm(20, 100))
|
||||
ma_ratio := pol.Def("ma_ratio", 3.8, core.PNorm(2, 6))
|
||||
rsi_length := pol.DefInt("rsi_length", 14, core.PNorm(7, 21))
|
||||
rsi_overbought := pol.Def("rsi_overbought", 70, core.PNorm(60, 80))
|
||||
rsi_oversold := pol.Def("rsi_oversold", 30, core.PNorm(20, 40))
|
||||
fib_period := pol.DefInt("fib_period", 50, core.PNorm(20, 100))
|
||||
|
||||
// 计算长期均线周期
|
||||
long_ma_length := int(float64(short_ma_length) * ma_ratio)
|
||||
|
||||
// 计算预热数量,取所有指标中最大的周期
|
||||
warmup_num := int(math.Max(float64(long_ma_length), float64(fib_period)))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmup_num,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多一个多头订单
|
||||
EachMaxShort: 1, // 最多一个空头订单
|
||||
TimeFrames: "8h",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化斐波那契回撤水平
|
||||
s.More = &FibRSIGoldenCross{
|
||||
FibHigh: math.NaN(),
|
||||
FibLow: math.NaN(),
|
||||
Fib38: math.NaN(),
|
||||
Fib50: math.NaN(),
|
||||
Fib61: math.NaN(),
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
fib, _ := s.More.(*FibRSIGoldenCross)
|
||||
|
||||
// 计算技术指标
|
||||
rsi := ta.RSI(e.Close, rsi_length)
|
||||
short_ma := ta.SMA(e.Close, short_ma_length)
|
||||
long_ma := ta.SMA(e.Close, long_ma_length)
|
||||
|
||||
// 获取当前价格
|
||||
close_price := e.Close.Get(0)
|
||||
if math.IsNaN(close_price) {
|
||||
return
|
||||
}
|
||||
|
||||
// 更新斐波那契回撤水平
|
||||
highest := ta.Highest(e.Close, fib_period).Get(0)
|
||||
lowest := ta.Lowest(e.Close, fib_period).Get(0)
|
||||
|
||||
// 如果最高价或最低价发生变化,更新斐波那契回撤水平
|
||||
if !math.IsNaN(highest) && (math.IsNaN(fib.FibHigh) || highest != fib.FibHigh) {
|
||||
fib.FibHigh = highest
|
||||
}
|
||||
if !math.IsNaN(lowest) && (math.IsNaN(fib.FibLow) || lowest != fib.FibLow) {
|
||||
fib.FibLow = lowest
|
||||
}
|
||||
|
||||
// 计算斐波那契回撤水平
|
||||
if !math.IsNaN(fib.FibHigh) && !math.IsNaN(fib.FibLow) {
|
||||
fib_range := fib.FibHigh - fib.FibLow
|
||||
fib.Fib38 = fib.FibHigh - fib_range*0.382
|
||||
fib.Fib50 = fib.FibHigh - fib_range*0.5
|
||||
fib.Fib61 = fib.FibHigh - fib_range*0.618
|
||||
}
|
||||
|
||||
// 获取当前指标值
|
||||
rsi_value := rsi.Get(0)
|
||||
short_ma_value := short_ma.Get(0)
|
||||
long_ma_value := long_ma.Get(0)
|
||||
|
||||
// 检查指标是否有效
|
||||
if math.IsNaN(rsi_value) || math.IsNaN(short_ma_value) || math.IsNaN(long_ma_value) ||
|
||||
math.IsNaN(fib.Fib50) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测交叉信号 - 只计算一次交叉信号
|
||||
cross_signal := short_ma.Cross(long_ma)
|
||||
golden_cross := cross_signal == 1 // 1表示上穿
|
||||
death_cross := cross_signal == -1 // -1表示下穿
|
||||
|
||||
// 检查当前持仓
|
||||
long_position := s.Position(1, "") > 0 // 1表示多头
|
||||
short_position := s.Position(-1, "") > 0 // -1表示空头
|
||||
|
||||
// 多头入场条件:黄金交叉且价格在50%斐波那契水平之上且RSI未超买
|
||||
long_condition := golden_cross && close_price > fib.Fib50 && rsi_value < rsi_overbought
|
||||
|
||||
// 空头入场条件:死亡交叉且价格在50%斐波那契水平之下且RSI未超卖
|
||||
short_condition := death_cross && close_price < fib.Fib50 && rsi_value > rsi_oversold
|
||||
|
||||
// 执行交易
|
||||
if long_condition && !long_position {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
})
|
||||
}
|
||||
|
||||
if short_condition && !short_position {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Sell",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 多头平仓条件:RSI超买
|
||||
if long_position && rsi_value > rsi_overbought {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Buy",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
// 空头平仓条件:RSI超卖
|
||||
if short_position && rsi_value < rsi_oversold {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Sell",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
160
llmjob/3939.go
Normal file
160
llmjob/3939.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J3939(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
lwstPeriod := pol.DefInt("lwst_period", 10, core.PNorm(5, 25))
|
||||
lwstMultiplier := pol.Def("lwst_multiplier", 3.0, core.PNorm(1.5, 5.5))
|
||||
lwstLength := pol.DefInt("lwst_length", 20, core.PNorm(10, 40))
|
||||
trendLength := pol.DefInt("trend_length", 14, core.PNorm(7, 25))
|
||||
trendThreshold := pol.Def("trend_threshold", 0.5, core.PNorm(0.1, 2.0))
|
||||
wtChannelLength := pol.DefInt("wt_channel_length", 9, core.PNorm(5, 20))
|
||||
wtAverageLength := pol.DefInt("wt_average_length", 12, core.PNorm(6, 24))
|
||||
wtMaLength := pol.DefInt("wt_ma_length", 3, core.PNorm(2, 8))
|
||||
wtOverbought := pol.Def("wt_overbought", 53, core.PNorm(40, 70))
|
||||
wtOversold := pol.Def("wt_oversold", -53, core.PNorm(-70, -40)) // 修复:独立的超卖参数
|
||||
slPercent := pol.Def("sl_percent", 2.0, core.PNorm(0.5, 5.0))
|
||||
tpPercent := pol.Def("tp_percent", 4.0, core.PNorm(1.0, 10.0))
|
||||
|
||||
warmupNum := max(lwstPeriod*2, trendLength*2, wtChannelLength+wtAverageLength)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "Multi-Indicator-Consensus",
|
||||
WarmupNum: warmupNum + 50,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1,
|
||||
EachMaxShort: 1,
|
||||
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
currentPrice := e.Close.Get(0)
|
||||
|
||||
// =================== Indicator 1: Liquidity Weighted Supertrend ===================
|
||||
// Volume-weighted component for dynamic sensitivity
|
||||
volSma := ta.SMA(e.Volume, lwstLength)
|
||||
volWeight := e.Volume.Div(volSma)
|
||||
|
||||
// ATR-based bands with volume weighting
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lwstPeriod)
|
||||
hl2 := ta.HL2(e.High, e.Low)
|
||||
_ = hl2.Add(atr.Mul(lwstMultiplier).Mul(volWeight)) // upperBand
|
||||
_ = hl2.Sub(atr.Mul(lwstMultiplier).Mul(volWeight)) // lowerBand
|
||||
|
||||
// Trend determination based on price action and previous trend
|
||||
// 修复:使用Series保存趋势状态
|
||||
lwstTrendSeries := e.Close.To("_lwst_trend", 0)
|
||||
var lwstTrend float64
|
||||
if e.Close.Len() > 1 {
|
||||
prevTrend := lwstTrendSeries.Get(1)
|
||||
if currentPrice > prevTrend {
|
||||
lwstTrend = 1
|
||||
} else if currentPrice < prevTrend {
|
||||
lwstTrend = -1
|
||||
} else {
|
||||
lwstTrend = prevTrend
|
||||
}
|
||||
} else {
|
||||
lwstTrend = 1.0
|
||||
}
|
||||
lwstTrendSeries.Set(lwstTrend)
|
||||
|
||||
// =================== Indicator 2: Trend Signals ===================
|
||||
// Dual EMA system for trend detection
|
||||
fastEma := ta.EMA(e.Close, trendLength)
|
||||
slowEma := ta.EMA(e.Close, trendLength*2)
|
||||
trendDiff := fastEma.Sub(slowEma).Div(slowEma).Mul(100)
|
||||
trendDiffVal := trendDiff.Get(0)
|
||||
|
||||
var trendSignal float64
|
||||
if trendDiffVal > trendThreshold {
|
||||
trendSignal = 1
|
||||
} else if trendDiffVal < -trendThreshold {
|
||||
trendSignal = -1
|
||||
} else {
|
||||
trendSignal = 0
|
||||
}
|
||||
|
||||
// =================== Indicator 3: Enhanced Wavetrend ===================
|
||||
// Calculate Wavetrend components
|
||||
ap := ta.HLC3(e.High, e.Low, e.Close)
|
||||
esa := ta.EMA(ap, wtChannelLength)
|
||||
d := ta.EMA(ap.Sub(esa).Abs(), wtChannelLength)
|
||||
ci := ap.Sub(esa).Div(d.Mul(0.015))
|
||||
tci := ta.EMA(ci, wtAverageLength)
|
||||
|
||||
// Generate main and signal lines
|
||||
wt1 := tci
|
||||
wt2 := ta.SMA(wt1, wtMaLength)
|
||||
|
||||
// Generate Wavetrend Signal based on overbought/oversold conditions
|
||||
wt1Val := wt1.Get(0)
|
||||
wt2Val := wt2.Get(0)
|
||||
|
||||
// 修复:使用Series保存wt_signal状态
|
||||
wtSignalSeries := e.Close.To("_wt_signal", 0)
|
||||
var wtSignal float64
|
||||
if wt1Val > wtOverbought && wt2Val > wtOverbought {
|
||||
wtSignal = -1 // 超买信号
|
||||
} else if wt1Val < wtOversold && wt2Val < wtOversold { // 修复:使用独立的wtOversold参数
|
||||
wtSignal = 1 // 超卖信号
|
||||
} else {
|
||||
wtSignal = wtSignalSeries.Get(1)
|
||||
}
|
||||
wtSignalSeries.Set(wtSignal)
|
||||
|
||||
// =================== Consensus Signal Generation ===================
|
||||
// 修复:正确计算净信号数(范围-3到3)
|
||||
consensusCount := 0
|
||||
if lwstTrend == 1 {
|
||||
consensusCount++
|
||||
} else if lwstTrend == -1 {
|
||||
consensusCount--
|
||||
}
|
||||
if trendSignal == 1 {
|
||||
consensusCount++
|
||||
} else if trendSignal == -1 {
|
||||
consensusCount--
|
||||
}
|
||||
if wtSignal == 1 {
|
||||
consensusCount++
|
||||
} else if wtSignal == -1 {
|
||||
consensusCount--
|
||||
}
|
||||
|
||||
// Generate trading signals when majority (2+ indicators) agree
|
||||
buySignal := consensusCount >= 2
|
||||
sellSignal := consensusCount <= -2
|
||||
|
||||
// =================== Trade Execution ===================
|
||||
if buySignal && len(s.LongOrders) == 0 && len(s.ShortOrders) == 0 {
|
||||
// 计算止损止盈价格
|
||||
slAmount := currentPrice * slPercent / 100
|
||||
tpAmount := currentPrice * tpPercent / 100
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
StopLossVal: slAmount,
|
||||
TakeProfitVal: tpAmount,
|
||||
})
|
||||
} else if sellSignal && len(s.ShortOrders) == 0 && len(s.LongOrders) == 0 {
|
||||
// 计算做空止损止盈价格
|
||||
slAmount := currentPrice * slPercent / 100
|
||||
tpAmount := currentPrice * tpPercent / 100
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Short: false,
|
||||
StopLossVal: slAmount,
|
||||
TakeProfitVal: tpAmount,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
109
llmjob/3955.go
Normal file
109
llmjob/3955.go
Normal file
@@ -0,0 +1,109 @@
|
||||
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 J3955(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
macdFastPeriod := pol.DefInt("macdFastPeriod", 14, core.PNorm(5, 20))
|
||||
macdSlowRate := pol.Def("macdSlowRate", 2, core.PNorm(1.5, 4))
|
||||
macdSignalPeriod := pol.DefInt("macdSignalPeriod", 9, core.PNorm(5, 15))
|
||||
vwmaFastPeriod := pol.DefInt("vwmaFastPeriod", 20, core.PNorm(10, 40))
|
||||
leverage := pol.DefInt("leverage", 1, core.PNorm(1, 20))
|
||||
|
||||
// 计算MACD慢线周期
|
||||
macdSlowPeriod := int(float64(macdFastPeriod) * macdSlowRate)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: macdSlowPeriod + macdSignalPeriod, // 确保有足够的数据计算指标
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多一个多头订单
|
||||
EachMaxShort: 1, // 最多一个空头订单
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算MACD指标
|
||||
macdLine, signalLine := ta.MACD(e.Close, macdFastPeriod, macdSlowPeriod, macdSignalPeriod)
|
||||
histogram := macdLine.Sub(signalLine) // 计算MACD柱状图
|
||||
|
||||
// 计算VWMA指标
|
||||
vwma20 := ta.VWMA(e.Close, e.Volume, vwmaFastPeriod) // 使用VWMA替代WMA,考虑成交量因素
|
||||
vwma50 := ta.VWMA(e.Close, e.Volume, 50) // 使用VWMA替代WMA,考虑成交量因素
|
||||
|
||||
// 获取当前指标值
|
||||
macdLineVal := macdLine.Get(0)
|
||||
signalLineVal := signalLine.Get(0)
|
||||
histogramVal := histogram.Get(0)
|
||||
vwma20Val := vwma20.Get(0)
|
||||
vwma50Val := vwma50.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(macdLineVal) || math.IsNaN(signalLineVal) || math.IsNaN(histogramVal) ||
|
||||
math.IsNaN(vwma20Val) || math.IsNaN(vwma50Val) {
|
||||
return
|
||||
}
|
||||
|
||||
// 生成MACD信号
|
||||
macdLongEntrySignal := histogramVal > 0
|
||||
macdShortEntrySignal := histogramVal < 0
|
||||
|
||||
// 生成VWMA信号
|
||||
vwmaLongEntrySignal := vwma20Val > vwma50Val
|
||||
vwmaShortEntrySignal := vwma20Val < vwma50Val
|
||||
|
||||
// 生成组合交易信号
|
||||
longEntry := macdLongEntrySignal && vwmaLongEntrySignal
|
||||
shortEntry := macdShortEntrySignal && vwmaShortEntrySignal
|
||||
|
||||
// 检测MACD交叉信号作为出场条件(优化为单次调用)
|
||||
cross := macdLine.Cross(signalLine)
|
||||
longExit := cross == -1 // MACD线下穿信号线
|
||||
shortExit := cross == 1 // MACD线上穿信号线
|
||||
|
||||
// 执行交易逻辑
|
||||
if longEntry && len(s.LongOrders) == 0 {
|
||||
// 计算杠杆化后的合约数量
|
||||
costRate := float64(leverage)
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Short: false,
|
||||
CostRate: costRate,
|
||||
Leverage: float64(leverage),
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
if longExit && len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "LongExit",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
if shortEntry && len(s.ShortOrders) == 0 {
|
||||
// 计算杠杆化后的合约数量
|
||||
costRate := float64(leverage)
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
CostRate: costRate,
|
||||
Leverage: float64(leverage),
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
if shortExit && len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "ShortExit",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
94
llmjob/3956.go
Normal file
94
llmjob/3956.go
Normal file
@@ -0,0 +1,94 @@
|
||||
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 J3956(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
fastLength := pol.DefInt("fastLength", 9, core.PNorm(5, 20))
|
||||
slowMaRate := pol.Def("slowMaRate", 2.17, core.PNorm(1.5, 4))
|
||||
signalLength := pol.DefInt("signalLength", 20, core.PNorm(9, 30))
|
||||
entryLevel := pol.Def("entryLevel", 0, core.PNorm(-5, 5))
|
||||
longExitLevel := pol.Def("longExitLevel", 11, core.PNorm(5, 20))
|
||||
shortExitLevel := pol.Def("shortExitLevel", -9, core.PNorm(-20, -5))
|
||||
|
||||
// 计算慢速EMA周期
|
||||
slowLength := int(float64(fastLength) * slowMaRate)
|
||||
|
||||
// 确定预热数量,取所有参数中的最大值
|
||||
warmupNum := fastLength
|
||||
if slowLength > warmupNum {
|
||||
warmupNum = slowLength
|
||||
}
|
||||
if signalLength > warmupNum {
|
||||
warmupNum = signalLength
|
||||
}
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算MACD指标
|
||||
fastEma := ta.EMA(e.Close, fastLength)
|
||||
slowEma := ta.EMA(e.Close, slowLength)
|
||||
macd := fastEma.Sub(slowEma)
|
||||
signal := ta.EMA(macd, signalLength)
|
||||
hist := macd.Sub(signal)
|
||||
|
||||
// 获取当前值
|
||||
histVal := hist.Get(0)
|
||||
if math.IsNaN(histVal) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查交叉信号
|
||||
entryCross := hist.Cross(entryLevel)
|
||||
longEntry := entryCross == 1 // 上穿入场水平线
|
||||
shortEntry := entryCross == -1 // 下穿入场水平线
|
||||
longExit := hist.Cross(longExitLevel) == 1 // 上穿多头出场水平线
|
||||
shortExit := hist.Cross(shortExitLevel) == -1 // 下穿空头出场水平线
|
||||
|
||||
// 获取当前持仓状态
|
||||
longPos := s.Position(core.OdDirtLong, "")
|
||||
shortPos := s.Position(core.OdDirtShort, "")
|
||||
|
||||
// 多头入场
|
||||
if longEntry && longPos == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
})
|
||||
}
|
||||
|
||||
// 空头入场
|
||||
if shortEntry && shortPos == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 多头出场
|
||||
if longPos > 0 && longExit {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Long Exit",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
// 空头出场
|
||||
if shortPos < 0 && shortExit {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Short Exit",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
113
llmjob/3992.go
Normal file
113
llmjob/3992.go
Normal file
@@ -0,0 +1,113 @@
|
||||
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 J3992(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastLength := pol.DefInt("fastLength", 10, core.PUniform(5, 20))
|
||||
macdSlowRate := pol.Def("macdSlowRate", 2.17, core.PNorm(1.5, 4))
|
||||
rsiThreshold := pol.DefInt("rsiThreshold", 65, core.PUniform(60, 80))
|
||||
bbMult := pol.Def("bbMult", 2.0, core.PNorm(1.5, 3.0))
|
||||
rsiLength := pol.DefInt("rsiLength", 14, core.PUniform(7, 21))
|
||||
|
||||
// 计算其他参数
|
||||
MACDLength := 9 // 固定值
|
||||
slowLength := int(float64(fastLength) * macdSlowRate)
|
||||
rsiOverbought := float64(rsiThreshold)
|
||||
rsiOversold := 100 - rsiOverbought
|
||||
stopLossPercent := 0.02 // 2% stop loss
|
||||
takeProfitPercent := 0.05 // 5% take profit
|
||||
|
||||
// 计算预热数量,取最大值
|
||||
warmupNum := slowLength
|
||||
if MACDLength > warmupNum {
|
||||
warmupNum = MACDLength
|
||||
}
|
||||
if rsiLength > warmupNum {
|
||||
warmupNum = rsiLength
|
||||
}
|
||||
if bbLength := 20; bbLength > warmupNum {
|
||||
warmupNum = bbLength
|
||||
}
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 检查数据长度是否足够
|
||||
if e.Close.Len() < warmupNum {
|
||||
return
|
||||
}
|
||||
|
||||
// MACD calculations
|
||||
fastMA := ta.EMA(e.Close, fastLength)
|
||||
slowMA := ta.EMA(e.Close, slowLength)
|
||||
macdLine := fastMA.Sub(slowMA)
|
||||
signalLine := ta.EMA(macdLine, MACDLength)
|
||||
|
||||
// RSI calculation
|
||||
rsi := ta.RSI(e.Close, rsiLength)
|
||||
|
||||
// Bollinger Bands calculation
|
||||
bbLength := 20
|
||||
basis := ta.SMA(e.Close, bbLength)
|
||||
stdDev := ta.StdDev(e.Close, bbLength)
|
||||
dev := stdDev.Mul(bbMult)
|
||||
upper := basis.Add(dev)
|
||||
lower := basis.Sub(dev)
|
||||
|
||||
// 获取当前值
|
||||
closePrice := e.Close.Get(0)
|
||||
macdVal := macdLine.Get(0)
|
||||
signalVal := signalLine.Get(0)
|
||||
rsiVal := rsi.Get(0)
|
||||
upperVal := upper.Get(0)
|
||||
lowerVal := lower.Get(0)
|
||||
|
||||
// 检查NaN值
|
||||
if math.IsNaN(closePrice) || math.IsNaN(macdVal) || math.IsNaN(signalVal) ||
|
||||
math.IsNaN(rsiVal) || math.IsNaN(upperVal) || math.IsNaN(lowerVal) {
|
||||
return
|
||||
}
|
||||
|
||||
// Entry conditions
|
||||
macdCrossOver := macdLine.Cross(signalLine) == 1
|
||||
rsiCrossUnder := rsi.Cross(rsiOversold) == -1
|
||||
longCondition := (macdCrossOver || rsiCrossUnder) && closePrice > lowerVal
|
||||
|
||||
macdCrossUnder := macdLine.Cross(signalLine) == -1
|
||||
rsiCrossOver := rsi.Cross(rsiOverbought) == 1
|
||||
shortCondition := (macdCrossUnder || rsiCrossOver) && closePrice < upperVal
|
||||
|
||||
// Long position logic
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: closePrice * (1 - stopLossPercent),
|
||||
TakeProfitVal: closePrice * (1 + takeProfitPercent),
|
||||
})
|
||||
}
|
||||
|
||||
// Short position logic
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: closePrice * (1 + stopLossPercent),
|
||||
TakeProfitVal: closePrice * (1 - takeProfitPercent),
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
177
llmjob/4009.go
Normal file
177
llmjob/4009.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// Sta4009 存储每个品种的策略状态
|
||||
type Sta4009 struct {
|
||||
atrTrailingStop float64 // ATR跟踪止损值
|
||||
position int // 当前持仓方向(1=多头,-1=空头,0=无持仓)
|
||||
entryPrice float64 // 入场价格
|
||||
buyTarget float64 // 多头目标价格
|
||||
sellTarget float64 // 空头目标价格
|
||||
}
|
||||
|
||||
func J4009(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
keyValue := pol.Def("keyValue", 1, core.PNorm(0.1, 5))
|
||||
atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(5, 30))
|
||||
useHeikinAshi := pol.DefInt("useHeikinAshi", 0, core.PNorm(0, 1)) // 0=false, 1=true
|
||||
exitPercentage := pol.Def("exitPercentage", 0.002, core.PNorm(0.001, 0.01))
|
||||
rsiPeriod := pol.DefInt("rsiPeriod", 14, core.PNorm(7, 30))
|
||||
donchianLength := pol.DefInt("donchianLength", 20, core.PNorm(10, 50))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: donchianLength + 20, // 确保有足够数据计算所有指标
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
TimeFrames: "1d",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略状态
|
||||
s.More = &Sta4009{
|
||||
atrTrailingStop: math.NaN(),
|
||||
position: 0,
|
||||
entryPrice: math.NaN(),
|
||||
buyTarget: math.NaN(),
|
||||
sellTarget: math.NaN(),
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*Sta4009)
|
||||
|
||||
// 计算基础指标
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
lossDistance := keyValue * atr.Get(0)
|
||||
rsiValue := ta.RSI(e.Close, rsiPeriod).Get(0)
|
||||
|
||||
// 计算价格源(普通收盘价或惠金-阿希收盘价)
|
||||
var priceSource *ta.Series
|
||||
if useHeikinAshi > 0 {
|
||||
haOpen, haHigh, haLow, haClose := ta.HeikinAshi(e)
|
||||
haCloseSeries := haOpen.Add(haHigh).Add(haLow).Add(haClose).Div(4)
|
||||
priceSource = haCloseSeries
|
||||
} else {
|
||||
priceSource = e.Close
|
||||
}
|
||||
currentPrice := priceSource.Get(0)
|
||||
prevPrice := priceSource.Get(1)
|
||||
|
||||
// 计算ATR跟踪止损
|
||||
if !math.IsNaN(lossDistance) && !math.IsNaN(currentPrice) && !math.IsNaN(prevPrice) {
|
||||
prevStop := state.atrTrailingStop
|
||||
if math.IsNaN(prevStop) {
|
||||
prevStop = 0
|
||||
}
|
||||
|
||||
if currentPrice > prevStop && prevPrice > prevStop {
|
||||
// 价格在止损线上方且前一价格也在止损线上方,止损线上移
|
||||
state.atrTrailingStop = math.Max(prevStop, currentPrice-lossDistance)
|
||||
} else if currentPrice < prevStop && prevPrice < prevStop {
|
||||
// 价格在止损线下方且前一价格也在止损线下方,止损线下移
|
||||
state.atrTrailingStop = math.Min(prevStop, currentPrice+lossDistance)
|
||||
} else if currentPrice > prevStop {
|
||||
// 价格上穿止损线,设置新的止损线
|
||||
state.atrTrailingStop = currentPrice - lossDistance
|
||||
} else {
|
||||
// 价格下穿止损线,设置新的止损线
|
||||
state.atrTrailingStop = currentPrice + lossDistance
|
||||
}
|
||||
}
|
||||
|
||||
// 计算持仓方向
|
||||
if !math.IsNaN(prevPrice) && !math.IsNaN(state.atrTrailingStop) {
|
||||
if prevPrice < state.atrTrailingStop && currentPrice > state.atrTrailingStop {
|
||||
state.position = 1 // 多头信号
|
||||
} else if prevPrice > state.atrTrailingStop && currentPrice < state.atrTrailingStop {
|
||||
state.position = -1 // 空头信号
|
||||
}
|
||||
// 否则保持原有方向
|
||||
}
|
||||
|
||||
// 计算唐奇安通道
|
||||
donchianUpper := ta.Highest(e.High, donchianLength)
|
||||
donchianLower := ta.Lowest(e.Low, donchianLength)
|
||||
donchianBasis := donchianUpper.Add(donchianLower).Div(2).Get(0)
|
||||
|
||||
// 计算交叉信号
|
||||
above := currentPrice > state.atrTrailingStop && prevPrice <= state.atrTrailingStop
|
||||
below := currentPrice < state.atrTrailingStop && prevPrice >= state.atrTrailingStop
|
||||
|
||||
// 生成交易信号
|
||||
buySignal := currentPrice > state.atrTrailingStop && above && rsiValue > 50 && currentPrice > donchianBasis
|
||||
sellSignal := currentPrice < state.atrTrailingStop && below && rsiValue < 50 && currentPrice < donchianBasis
|
||||
|
||||
// 执行交易
|
||||
if buySignal && s.OrderNum == 0 {
|
||||
state.entryPrice = currentPrice
|
||||
state.buyTarget = state.entryPrice * (1 + exitPercentage)
|
||||
state.sellTarget = state.entryPrice * (1 - exitPercentage)
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
} else if sellSignal && s.OrderNum == 0 {
|
||||
state.entryPrice = currentPrice
|
||||
state.buyTarget = state.entryPrice * (1 + exitPercentage)
|
||||
state.sellTarget = state.entryPrice * (1 - exitPercentage)
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Sell",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
// 退出条件检查
|
||||
if len(s.LongOrders) > 0 {
|
||||
od := s.LongOrders[0]
|
||||
if od.Status >= ormo.InOutStatusFullEnter {
|
||||
if currentPrice >= state.buyTarget {
|
||||
// 达到止盈目标
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Take Profit",
|
||||
Dirt: core.OdDirtLong,
|
||||
Limit: state.buyTarget,
|
||||
})
|
||||
} else if currentPrice <= state.sellTarget {
|
||||
// 触发止损
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Stop Loss",
|
||||
Dirt: core.OdDirtLong,
|
||||
Limit: state.sellTarget,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.ShortOrders) > 0 {
|
||||
od := s.ShortOrders[0]
|
||||
if od.Status >= ormo.InOutStatusFullEnter {
|
||||
if currentPrice <= state.sellTarget {
|
||||
// 达到止盈目标
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Take Profit",
|
||||
Dirt: core.OdDirtShort,
|
||||
Limit: state.sellTarget,
|
||||
})
|
||||
} else if currentPrice >= state.buyTarget {
|
||||
// 触发止损
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Stop Loss",
|
||||
Dirt: core.OdDirtShort,
|
||||
Limit: state.buyTarget,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
128
llmjob/4034.go
Normal file
128
llmjob/4034.go
Normal file
@@ -0,0 +1,128 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
)
|
||||
|
||||
// 定义一个结构体来存储策略需要的变量
|
||||
type StrategyData struct {
|
||||
atrValue float64
|
||||
}
|
||||
|
||||
func J4034(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
fastLength := pol.DefInt("fastLength", 12, core.PNorm(5, 20))
|
||||
emaRate := pol.Def("emaRate", 2.33, core.PNorm(1.5, 5.0))
|
||||
rsiLength := pol.DefInt("rsiLength", 14, core.PNorm(7, 30))
|
||||
rsiOverbought := pol.DefInt("rsiOverbought", 70, core.PNorm(60, 85))
|
||||
rsiOversold := pol.DefInt("rsiOversold", 30, core.PNorm(15, 40))
|
||||
volLength := pol.DefInt("volLength", 20, core.PNorm(10, 40))
|
||||
atrLength := pol.DefInt("atrLength", 14, core.PNorm(7, 30))
|
||||
atrMultiplierSL := pol.Def("atrMultiplierSL", 1.8, core.PNorm(1.0, 3.0))
|
||||
atrMultiplierTP := pol.Def("atrMultiplierTP", 2.5, core.PNorm(1.5, 5.0))
|
||||
|
||||
// 计算慢速EMA周期
|
||||
slowLength := int(float64(fastLength) * emaRate)
|
||||
|
||||
// 确定预热数量,取所有指标中最大的周期
|
||||
warmupNum := fastLength
|
||||
if slowLength > warmupNum {
|
||||
warmupNum = slowLength
|
||||
}
|
||||
if rsiLength > warmupNum {
|
||||
warmupNum = rsiLength
|
||||
}
|
||||
if volLength > warmupNum {
|
||||
warmupNum = volLength
|
||||
}
|
||||
if atrLength > warmupNum {
|
||||
warmupNum = atrLength
|
||||
}
|
||||
// 额外增加1个K线用于突破确认
|
||||
warmupNum += 1
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
EachMaxLong: 1, // 最多同时持有一个多头订单
|
||||
EachMaxShort: 1, // 最多同时持有一个空头订单
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略数据
|
||||
s.More = &StrategyData{atrValue: 0}
|
||||
},
|
||||
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算技术指标
|
||||
fastEMA := ta.EMA(e.Close, fastLength)
|
||||
slowEMA := ta.EMA(e.Close, slowLength)
|
||||
rsiValue := ta.RSI(e.Close, rsiLength)
|
||||
volMA := ta.SMA(e.Volume, volLength)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrLength)
|
||||
|
||||
// 获取当前值
|
||||
closePrice := e.Close.Get(0)
|
||||
highPrev := e.High.Get(1)
|
||||
lowPrev := e.Low.Get(1)
|
||||
volume := e.Volume.Get(0)
|
||||
volMAValue := volMA.Get(0)
|
||||
rsi := rsiValue.Get(0)
|
||||
atrValue := atr.Get(0)
|
||||
maCross := fastEMA.Cross(slowEMA)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(closePrice) || math.IsNaN(highPrev) || math.IsNaN(lowPrev) ||
|
||||
math.IsNaN(volume) || math.IsNaN(volMAValue) || math.IsNaN(rsi) || math.IsNaN(atrValue) {
|
||||
return
|
||||
}
|
||||
|
||||
// 存储ATR值以便在跟踪止损中使用
|
||||
data, _ := s.More.(*StrategyData)
|
||||
data.atrValue = atrValue
|
||||
|
||||
// K线突破条件
|
||||
longCandleBreak := closePrice > highPrev
|
||||
shortCandleBreak := closePrice < lowPrev
|
||||
|
||||
// 入场条件
|
||||
longCondition := maCross == 1 && rsi < float64(rsiOverbought) && volume > volMAValue && longCandleBreak
|
||||
shortCondition := maCross == -1 && rsi > float64(rsiOversold) && volume > volMAValue && shortCandleBreak
|
||||
|
||||
// 执行交易
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
// 计算具体止损止盈价格
|
||||
longStop := closePrice - atrMultiplierSL*atrValue
|
||||
longTP := closePrice + atrMultiplierTP*atrValue
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLoss: longStop,
|
||||
TakeProfit: longTP,
|
||||
})
|
||||
}
|
||||
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
// 计算具体止损止盈价格
|
||||
shortStop := closePrice + atrMultiplierSL*atrValue
|
||||
shortTP := closePrice - atrMultiplierTP*atrValue
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLoss: shortStop,
|
||||
TakeProfit: shortTP,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
477
llmjob/4067.go
Normal file
477
llmjob/4067.go
Normal file
@@ -0,0 +1,477 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// State4067 存储每个品种的策略状态
|
||||
type State4067 struct {
|
||||
step int
|
||||
inTrade bool
|
||||
tradeDirection int // 1=多头, -1=空头, 0=无
|
||||
entryPrice float64
|
||||
tp1Hit bool
|
||||
buytp1Hit bool
|
||||
buySLPrice float64
|
||||
buyTP1Price float64
|
||||
buyTP2Price float64
|
||||
selltp1Hit bool
|
||||
sellSLPrice float64
|
||||
sellTP1Price float64
|
||||
sellTP2Price float64
|
||||
higherEMA float64 // 更高时间周期EMA值
|
||||
}
|
||||
|
||||
// MTFCross 多时间周期EMA交叉策略
|
||||
func J4067(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
fastPeriod := pol.DefInt("fastPeriod", 9, core.PNorm(5, 20))
|
||||
slowRate := pol.Def("slowRate", 5.56, core.PNorm(2, 10))
|
||||
higherEMARate := pol.Def("higherEMARate", 2, core.PNorm(1.5, 5))
|
||||
slRate := pol.Def("slRate", 1.6, core.PNorm(0.5, 5))
|
||||
tp1Rate := pol.Def("tp1Rate", 1.4, core.PNorm(0.5, 3))
|
||||
beoffRate := pol.Def("beoffRate", 0.3, core.PNorm(0.1, 1))
|
||||
atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 30))
|
||||
|
||||
// 交易时间控制参数
|
||||
startHour := pol.DefInt("startHour", 1, core.PNorm(0, 23))
|
||||
endHour := pol.DefInt("endHour", 25, core.PNorm(0, 47))
|
||||
tradeOnMonday := pol.DefInt("tradeOnMonday", 1, core.PNorm(0, 1)) != 0
|
||||
tradeOnTuesday := pol.DefInt("tradeOnTuesday", 1, core.PNorm(0, 1)) != 0
|
||||
tradeOnWednesday := pol.DefInt("tradeOnWednesday", 1, core.PNorm(0, 1)) != 0
|
||||
tradeOnThursday := pol.DefInt("tradeOnThursday", 1, core.PNorm(0, 1)) != 0
|
||||
tradeOnFriday := pol.DefInt("tradeOnFriday", 1, core.PNorm(0, 1)) != 0
|
||||
|
||||
// 计算衍生参数
|
||||
slowPeriod := int(float64(fastPeriod) * slowRate)
|
||||
higherPeriod := int(float64(slowPeriod) * higherEMARate)
|
||||
|
||||
// 调整endHour为24小时格式
|
||||
adjustedEndHour := endHour % 24
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowPeriod, // 预热K线数量
|
||||
HedgeOff: true, // 单向持仓
|
||||
EachMaxLong: 1, // 最多一个多头订单
|
||||
EachMaxShort: 1, // 最多一个空头订单
|
||||
RunTimeFrames: []string{"4h"},
|
||||
// 初始化策略状态
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &State4067{
|
||||
step: 0,
|
||||
inTrade: false,
|
||||
tradeDirection: 0,
|
||||
entryPrice: math.NaN(),
|
||||
tp1Hit: false,
|
||||
buytp1Hit: false,
|
||||
selltp1Hit: false,
|
||||
buySLPrice: math.NaN(),
|
||||
buyTP1Price: math.NaN(),
|
||||
buyTP2Price: math.NaN(),
|
||||
sellSLPrice: math.NaN(),
|
||||
sellTP1Price: math.NaN(),
|
||||
sellTP2Price: math.NaN(),
|
||||
higherEMA: math.NaN(),
|
||||
}
|
||||
},
|
||||
|
||||
// 定义需要订阅的其他时间周期数据
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
return []*strat.PairSub{
|
||||
{Pair: "_cur_", TimeFrame: "15m", WarmupNum: higherPeriod},
|
||||
}
|
||||
},
|
||||
|
||||
// 处理更高时间周期数据
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
|
||||
state, _ := s.More.(*State4067)
|
||||
// 计算更高时间周期的EMA
|
||||
higherEMA := ta.EMA(e.Close, higherPeriod)
|
||||
if higherEMA.Len() > 0 {
|
||||
state.higherEMA = higherEMA.Get(0)
|
||||
}
|
||||
},
|
||||
|
||||
// 主策略逻辑
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*State4067)
|
||||
|
||||
// 计算技术指标
|
||||
fastEMA := ta.EMA(e.Close, fastPeriod)
|
||||
slowEMA := ta.EMA(e.Close, slowPeriod)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
|
||||
// 获取当前值
|
||||
closePrice := e.Close.Get(0)
|
||||
fastEMAV := fastEMA.Get(0)
|
||||
slowEMAV := slowEMA.Get(0)
|
||||
atrV := atr.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(closePrice) || math.IsNaN(fastEMAV) || math.IsNaN(slowEMAV) ||
|
||||
math.IsNaN(atrV) || math.IsNaN(state.higherEMA) {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算止损止盈距离
|
||||
slDist := atrV * slRate
|
||||
tp1Dist := atrV * tp1Rate
|
||||
tp2Dist := atrV * tp1Rate * 2 // 第二止盈是第一止盈的2倍
|
||||
beoffDist := atrV * beoffRate
|
||||
|
||||
// 检查交易时间
|
||||
t := time.UnixMilli(e.TimeStart)
|
||||
isTradingTime := checkTradingTime(t, startHour, adjustedEndHour,
|
||||
tradeOnMonday, tradeOnTuesday, tradeOnWednesday, tradeOnThursday, tradeOnFriday)
|
||||
|
||||
// 更新信号状态步骤
|
||||
updateStep(state, fastEMA, slowEMA)
|
||||
|
||||
// 生成交易信号(仅在交易时间内)
|
||||
if isTradingTime {
|
||||
if state.step == 1 && !state.inTrade && fastEMAV > slowEMAV && fastEMAV > state.higherEMA {
|
||||
// 买入信号
|
||||
state.inTrade = true
|
||||
state.tradeDirection = 1
|
||||
state.entryPrice = closePrice
|
||||
state.buytp1Hit = false
|
||||
state.tp1Hit = false
|
||||
state.buySLPrice = state.entryPrice - slDist
|
||||
state.buyTP1Price = state.entryPrice + tp1Dist
|
||||
state.buyTP2Price = state.entryPrice + tp2Dist
|
||||
state.step = 3
|
||||
|
||||
// 开多单
|
||||
//s.OpenOrder(&strat.EnterReq{
|
||||
// Tag: "Buy",
|
||||
// Short: false,
|
||||
// StopLoss: state.buySLPrice,
|
||||
// TakeProfit: state.buyTP1Price,
|
||||
// StopLossVal: slDist,
|
||||
//})
|
||||
} else if state.step == 2 && !state.inTrade && fastEMAV < slowEMAV && fastEMAV < state.higherEMA {
|
||||
// 卖出信号
|
||||
state.inTrade = true
|
||||
state.tradeDirection = -1
|
||||
state.entryPrice = closePrice
|
||||
state.selltp1Hit = false
|
||||
state.tp1Hit = false
|
||||
state.sellSLPrice = state.entryPrice + slDist
|
||||
state.sellTP1Price = state.entryPrice - tp1Dist
|
||||
state.sellTP2Price = state.entryPrice - tp2Dist
|
||||
state.step = 4
|
||||
|
||||
// 开空单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Sell",
|
||||
Short: true,
|
||||
StopLoss: state.sellSLPrice,
|
||||
TakeProfit: state.sellTP1Price,
|
||||
StopLossVal: slDist,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 管理多头仓位
|
||||
if state.inTrade && state.tradeDirection == 1 && len(s.LongOrders) > 0 {
|
||||
od := s.LongOrders[0]
|
||||
highPrice := e.High.Get(0)
|
||||
lowPrice := e.Low.Get(0)
|
||||
|
||||
// 检查是否达到第一止盈目标
|
||||
if highPrice >= state.buyTP1Price && !state.tp1Hit {
|
||||
state.tp1Hit = true
|
||||
state.buytp1Hit = true
|
||||
// 移动止损到保本位置
|
||||
state.buySLPrice = state.entryPrice + beoffDist
|
||||
// 平仓25%
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP1 Hit",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: 0.25,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
// 更新剩余仓位的止损止盈
|
||||
od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: state.buySLPrice,
|
||||
Rate: 1.0,
|
||||
Tag: "Breakeven",
|
||||
})
|
||||
od.SetTakeProfit(&ormo.ExitTrigger{
|
||||
Price: state.buyTP2Price,
|
||||
Rate: 1.0,
|
||||
Tag: "TP2",
|
||||
})
|
||||
}
|
||||
|
||||
// 检查是否触发止损
|
||||
if lowPrice <= state.buySLPrice && !state.tp1Hit {
|
||||
// 止损出场
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "SL Hit",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: 1.0,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
resetState(state)
|
||||
}
|
||||
|
||||
// 检查是否达到第二止盈目标
|
||||
if state.tp1Hit && highPrice >= state.buyTP2Price {
|
||||
// 第二止盈出场
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP2 Hit",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: 1.0,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
resetState(state)
|
||||
}
|
||||
|
||||
// 检查保本止损是否触发
|
||||
if state.tp1Hit && lowPrice <= state.buySLPrice {
|
||||
// 保本止损出场
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Breakeven Hit",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: 1.0,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
resetState(state)
|
||||
}
|
||||
}
|
||||
|
||||
// 管理空头仓位
|
||||
if state.inTrade && state.tradeDirection == -1 && len(s.ShortOrders) > 0 {
|
||||
od := s.ShortOrders[0]
|
||||
highPrice := e.High.Get(0)
|
||||
lowPrice := e.Low.Get(0)
|
||||
|
||||
// 检查是否达到第一止盈目标
|
||||
if lowPrice <= state.sellTP1Price && !state.tp1Hit {
|
||||
state.tp1Hit = true
|
||||
state.selltp1Hit = true
|
||||
// 移动止损到保本位置
|
||||
state.sellSLPrice = state.entryPrice - beoffDist
|
||||
// 平仓25%
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP1 Hit",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: 0.25,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
// 更新剩余仓位的止损止盈
|
||||
od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: state.sellSLPrice,
|
||||
Rate: 1.0,
|
||||
Tag: "Breakeven",
|
||||
})
|
||||
od.SetTakeProfit(&ormo.ExitTrigger{
|
||||
Price: state.sellTP2Price,
|
||||
Rate: 1.0,
|
||||
Tag: "TP2",
|
||||
})
|
||||
}
|
||||
|
||||
// 检查是否触发止损
|
||||
if highPrice >= state.sellSLPrice && !state.tp1Hit {
|
||||
// 止损出场
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "SL Hit",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: 1.0,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
resetState(state)
|
||||
}
|
||||
|
||||
// 检查是否达到第二止盈目标
|
||||
if state.tp1Hit && lowPrice <= state.sellTP2Price {
|
||||
// 第二止盈出场
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "TP2 Hit",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: 1.0,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
resetState(state)
|
||||
}
|
||||
|
||||
// 检查保本止损是否触发
|
||||
if state.tp1Hit && highPrice >= state.sellSLPrice {
|
||||
// 保本止损出场
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Breakeven Hit",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: 1.0,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
resetState(state)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新交易状态
|
||||
if state.inTrade && state.tradeDirection == 1 && state.step == 2 {
|
||||
// 多头收到卖出信号
|
||||
state.inTrade = false
|
||||
state.tradeDirection = 0
|
||||
state.step = 2
|
||||
}
|
||||
|
||||
if state.inTrade && state.tradeDirection == -1 && state.step == 1 {
|
||||
// 空头收到买入信号
|
||||
state.inTrade = false
|
||||
state.tradeDirection = 0
|
||||
state.step = 1
|
||||
}
|
||||
},
|
||||
|
||||
// 自定义订单退出逻辑
|
||||
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
|
||||
state, _ := s.More.(*State4067)
|
||||
|
||||
// 多头订单检查
|
||||
if !od.Short && state.inTrade && state.tradeDirection == 1 {
|
||||
lowPrice := s.Env.Low.Get(0)
|
||||
|
||||
// 检查是否触发止损
|
||||
if lowPrice <= state.buySLPrice {
|
||||
return &strat.ExitReq{
|
||||
Tag: "SL Hit",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否达到第二止盈目标
|
||||
if state.tp1Hit {
|
||||
highPrice := s.Env.High.Get(0)
|
||||
if highPrice >= state.buyTP2Price {
|
||||
return &strat.ExitReq{
|
||||
Tag: "TP2 Hit",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 空头订单检查
|
||||
if od.Short && state.inTrade && state.tradeDirection == -1 {
|
||||
highPrice := s.Env.High.Get(0)
|
||||
|
||||
// 检查是否触发止损
|
||||
if highPrice >= state.sellSLPrice {
|
||||
return &strat.ExitReq{
|
||||
Tag: "SL Hit",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否达到第二止盈目标
|
||||
if state.tp1Hit {
|
||||
lowPrice := s.Env.Low.Get(0)
|
||||
if lowPrice <= state.sellTP2Price {
|
||||
return &strat.ExitReq{
|
||||
Tag: "TP2 Hit",
|
||||
Dirt: core.OdDirtShort,
|
||||
ExitRate: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// checkTradingTime 检查当前时间是否在交易时间内
|
||||
func checkTradingTime(t time.Time, startHour, adjustedEndHour int,
|
||||
tradeOnMonday, tradeOnTuesday, tradeOnWednesday, tradeOnThursday, tradeOnFriday bool) bool {
|
||||
// 获取当前小时和星期几
|
||||
currentHour := t.Hour()
|
||||
currentDay := t.Weekday()
|
||||
|
||||
// 检查是否在交易小时内
|
||||
isTradingHour := false
|
||||
if startHour < adjustedEndHour {
|
||||
isTradingHour = currentHour >= startHour && currentHour < adjustedEndHour
|
||||
} else {
|
||||
isTradingHour = currentHour >= startHour || currentHour < adjustedEndHour
|
||||
}
|
||||
|
||||
// 检查是否在交易日内
|
||||
isTradingDay := false
|
||||
switch currentDay {
|
||||
case time.Monday:
|
||||
isTradingDay = tradeOnMonday
|
||||
case time.Tuesday:
|
||||
isTradingDay = tradeOnTuesday
|
||||
case time.Wednesday:
|
||||
isTradingDay = tradeOnWednesday
|
||||
case time.Thursday:
|
||||
isTradingDay = tradeOnThursday
|
||||
case time.Friday:
|
||||
isTradingDay = tradeOnFriday
|
||||
}
|
||||
|
||||
return isTradingHour && isTradingDay
|
||||
}
|
||||
|
||||
// updateStep 更新信号状态步骤
|
||||
func updateStep(state *State4067, fastEMA, slowEMA *ta.Series) {
|
||||
// 检查EMA交叉
|
||||
crossUp := fastEMA.Cross(slowEMA)
|
||||
crossDown := slowEMA.Cross(fastEMA)
|
||||
|
||||
// 更新状态步骤
|
||||
if (state.step == 0 || state.step == 4) && crossUp == 1 && fastEMA.Get(0) > state.higherEMA {
|
||||
state.step = 1
|
||||
}
|
||||
|
||||
if state.step == 3 && crossUp == 1 && fastEMA.Get(0) > state.higherEMA {
|
||||
state.step = 3
|
||||
}
|
||||
|
||||
if state.step == 2 && crossUp == 1 && fastEMA.Get(0) > state.higherEMA {
|
||||
state.step = 1
|
||||
}
|
||||
|
||||
if (state.step == 0 || state.step == 3) && crossDown == 1 && fastEMA.Get(0) < state.higherEMA {
|
||||
state.step = 2
|
||||
}
|
||||
|
||||
if state.step == 4 && crossDown == 1 && fastEMA.Get(0) < state.higherEMA {
|
||||
state.step = 4
|
||||
}
|
||||
|
||||
if state.step == 1 && crossDown == 1 && fastEMA.Get(0) < state.higherEMA {
|
||||
state.step = 2
|
||||
}
|
||||
}
|
||||
|
||||
// resetState 重置策略状态
|
||||
func resetState(state *State4067) {
|
||||
state.inTrade = false
|
||||
state.tradeDirection = 0
|
||||
state.entryPrice = math.NaN()
|
||||
state.tp1Hit = false
|
||||
state.buytp1Hit = false
|
||||
state.selltp1Hit = false
|
||||
state.buySLPrice = math.NaN()
|
||||
state.buyTP1Price = math.NaN()
|
||||
state.buyTP2Price = math.NaN()
|
||||
state.sellSLPrice = math.NaN()
|
||||
state.sellTP1Price = math.NaN()
|
||||
state.sellTP2Price = math.NaN()
|
||||
state.step = 0
|
||||
}
|
||||
134
llmjob/419.go
Normal file
134
llmjob/419.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
type FLIData struct {
|
||||
trendLine float64
|
||||
iTrend float64
|
||||
}
|
||||
|
||||
func J419(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
bbPeriod := pol.DefInt("bbPeriod", 38, core.PNorm(10, 50))
|
||||
bbDeviations := pol.Def("bbDeviations", 0.65, core.PNorm(0.5, 3.0))
|
||||
useATRFilter := pol.Def("useATRFilter", 0.0, core.PUniform(0, 1)) // 0=false, 1=true
|
||||
atrPeriod := pol.DefInt("atrPeriod", 5, core.PNorm(3, 20))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: bbPeriod + 5, // 确保有足够数据计算指标
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
TimeFrames: "4h",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的数据
|
||||
s.More = &FLIData{
|
||||
trendLine: 0.0,
|
||||
iTrend: 0.0,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
data, _ := s.More.(*FLIData)
|
||||
|
||||
// 计算布林带
|
||||
bbMiddle := ta.SMA(e.Close, bbPeriod)
|
||||
stdDev := ta.StdDev(e.Close, bbPeriod)
|
||||
bbUpper := bbMiddle.Add(stdDev.Mul(bbDeviations))
|
||||
bbLower := bbMiddle.Sub(stdDev.Mul(bbDeviations))
|
||||
|
||||
// 获取当前价格
|
||||
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) {
|
||||
return
|
||||
}
|
||||
|
||||
// 确定布林带信号
|
||||
bbSignal := 0
|
||||
if closePrice > bbUpper.Get(0) {
|
||||
bbSignal = 1 // 看涨信号
|
||||
} else if closePrice < bbLower.Get(0) {
|
||||
bbSignal = -1 // 看跌信号
|
||||
}
|
||||
|
||||
// 计算趋势线
|
||||
prevTrendLine := data.trendLine
|
||||
var newTrendLine float64
|
||||
|
||||
if useATRFilter > 0.5 { // 启用ATR过滤器
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
atrValue := atr.Get(0)
|
||||
|
||||
switch bbSignal {
|
||||
case 1: // 看涨信号
|
||||
newTrendLine = lowPrice - atrValue
|
||||
if newTrendLine < prevTrendLine {
|
||||
newTrendLine = prevTrendLine
|
||||
}
|
||||
case -1: // 看跌信号
|
||||
newTrendLine = highPrice + atrValue
|
||||
if newTrendLine > prevTrendLine {
|
||||
newTrendLine = prevTrendLine
|
||||
}
|
||||
default: // 无信号
|
||||
newTrendLine = prevTrendLine
|
||||
}
|
||||
} else { // 不启用ATR过滤器
|
||||
switch bbSignal {
|
||||
case 1: // 看涨信号
|
||||
newTrendLine = lowPrice
|
||||
if newTrendLine < prevTrendLine {
|
||||
newTrendLine = prevTrendLine
|
||||
}
|
||||
case -1: // 看跌信号
|
||||
newTrendLine = highPrice
|
||||
if newTrendLine > prevTrendLine {
|
||||
newTrendLine = prevTrendLine
|
||||
}
|
||||
default: // 无信号
|
||||
newTrendLine = prevTrendLine
|
||||
}
|
||||
}
|
||||
|
||||
// 更新趋势线
|
||||
data.trendLine = newTrendLine
|
||||
|
||||
// 确定趋势方向
|
||||
prevITrend := data.iTrend
|
||||
var newITrend float64
|
||||
|
||||
if newTrendLine > prevTrendLine {
|
||||
newITrend = 1 // 上升趋势
|
||||
} else if newTrendLine < prevTrendLine {
|
||||
newITrend = -1 // 下降趋势
|
||||
} else {
|
||||
newITrend = prevITrend // 保持原趋势
|
||||
}
|
||||
|
||||
// 生成交易信号
|
||||
if prevITrend == -1 && newITrend == 1 {
|
||||
// 趋势从下跌转为上涨,产生买入信号
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "buy",
|
||||
})
|
||||
} else if prevITrend == 1 && newITrend == -1 {
|
||||
// 趋势从上涨转为下跌,产生卖出信号
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "sell",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 更新趋势方向
|
||||
data.iTrend = newITrend
|
||||
},
|
||||
}
|
||||
}
|
||||
110
llmjob/4217.go
Normal file
110
llmjob/4217.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J4217(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
fastLength := pol.DefInt("fastLength", 9, core.PNorm(5, 20))
|
||||
maRate := pol.Def("maRate", 2.33, core.PNorm(1.5, 5))
|
||||
rsiLength := pol.DefInt("rsiLength", 14, core.PNorm(7, 21))
|
||||
rsiThreshold := pol.DefInt("rsiThreshold", 70, core.PNorm(60, 80))
|
||||
atrStopLoss := pol.Def("atrStopLoss", 2.0, core.PNorm(1.0, 4.0))
|
||||
atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 21))
|
||||
// 添加MACD参数配置
|
||||
macdFast := pol.DefInt("macdFast", 12, core.PNorm(8, 20))
|
||||
macdSlow := pol.DefInt("macdSlow", 26, core.PNorm(20, 40))
|
||||
macdSignal := pol.DefInt("macdSignal", 9, core.PNorm(5, 15))
|
||||
|
||||
// 计算慢速EMA周期
|
||||
slowLength := int(float64(fastLength) * maRate)
|
||||
|
||||
// 计算RSI超买超卖水平
|
||||
rsiOverboughtLevel := float64(rsiThreshold)
|
||||
rsiOversoldLevel := 100 - float64(rsiThreshold)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowLength + atrPeriod, // 确保有足够的数据计算指标
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多开1个多单
|
||||
EachMaxShort: 1, // 最多开1个空单
|
||||
TimeFrames: "8h",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算技术指标
|
||||
fastMA := ta.EMA(e.Close, fastLength)
|
||||
slowMA := ta.EMA(e.Close, slowLength)
|
||||
rsi := ta.RSI(e.Close, rsiLength)
|
||||
// 使用配置的MACD参数
|
||||
macdLine, signalLine := ta.MACD(e.Close, macdFast, macdSlow, macdSignal)
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
|
||||
// 获取最新值
|
||||
fastMAVal := fastMA.Get(0)
|
||||
slowMAVal := slowMA.Get(0)
|
||||
rsiVal := rsi.Get(0)
|
||||
atrVal := atr.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(fastMAVal) || math.IsNaN(slowMAVal) || math.IsNaN(rsiVal) || math.IsNaN(atrVal) {
|
||||
return
|
||||
}
|
||||
|
||||
// 判断趋势
|
||||
bullishTrend := fastMAVal > slowMAVal
|
||||
bearishTrend := fastMAVal < slowMAVal
|
||||
|
||||
// 生成交易信号
|
||||
longCondition := bullishTrend && rsiVal < rsiOverboughtLevel && macdLine.Cross(signalLine) == 1
|
||||
shortCondition := bearishTrend && rsiVal > rsiOversoldLevel && macdLine.Cross(signalLine) == -1
|
||||
|
||||
// 计算止损距离
|
||||
stopLossDistance := atrVal * atrStopLoss
|
||||
|
||||
// 执行交易操作
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
// 开多单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Short: false,
|
||||
StopLossVal: stopLossDistance,
|
||||
StopLossTag: "ATR Stop Loss",
|
||||
})
|
||||
}
|
||||
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
// 开空单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
StopLossVal: stopLossDistance,
|
||||
StopLossTag: "ATR Stop Loss",
|
||||
})
|
||||
}
|
||||
|
||||
// 平仓条件
|
||||
if len(s.LongOrders) > 0 && (bearishTrend || rsiVal > rsiOverboughtLevel) {
|
||||
// 平多单
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Close Long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
if len(s.ShortOrders) > 0 && (bullishTrend || rsiVal < rsiOversoldLevel) {
|
||||
// 平空单
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Close Short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
113
llmjob/4225.go
Normal file
113
llmjob/4225.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
// StrategyMore 存储每个品种的策略状态
|
||||
type StrategyMore struct {
|
||||
LastBarNum float64 // 上次交易的K线编号
|
||||
}
|
||||
|
||||
// MultiEMACross 多重均线交叉与时间间隔整合策略
|
||||
func J4225(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
emaBasePeriod := pol.DefInt("emaBasePeriod", 4, core.PNorm(3, 15))
|
||||
emaMidRatio := pol.Def("emaMidRatio", 2.1, core.PNorm(1.5, 4))
|
||||
emaSlowRatio := pol.Def("emaSlowRatio", 10, core.PNorm(5, 20))
|
||||
timeInterval := pol.DefInt("timeInterval", 35, core.PNorm(10, 100))
|
||||
atrMultiplier := pol.Def("atrMultiplier", 1.9, core.PNorm(0.5, 5))
|
||||
|
||||
// 计算EMA周期
|
||||
emaMidPeriod := int(float64(emaBasePeriod) * emaMidRatio)
|
||||
emaSlowPeriod := int(float64(emaBasePeriod) * emaSlowRatio)
|
||||
|
||||
// 确定预热K线数量,取最慢EMA周期的2倍
|
||||
warmupNum := emaSlowPeriod * 2
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的策略状态
|
||||
s.More = &StrategyMore{
|
||||
LastBarNum: -1, // 初始化为-1,表示尚未交易
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
m, _ := s.More.(*StrategyMore)
|
||||
|
||||
// 计算EMA指标
|
||||
emaFast := ta.EMA(e.Close, emaBasePeriod)
|
||||
emaMid := ta.EMA(e.Close, emaMidPeriod)
|
||||
emaSlow := ta.EMA(e.Close, emaSlowPeriod)
|
||||
|
||||
// 计算ATR用于动态止盈止损
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, 14)
|
||||
atrVal := atr.Get(0)
|
||||
|
||||
// 检查EMA交叉条件 - 修复重复计算问题
|
||||
fastCrossSlow := emaFast.Cross(emaSlow)
|
||||
midCrossSlow := emaMid.Cross(emaSlow)
|
||||
fastCrossSlowUp := fastCrossSlow == 1 // 快线上穿慢线
|
||||
midCrossSlowUp := midCrossSlow == 1 // 中线上穿慢线
|
||||
fastCrossSlowDown := fastCrossSlow == -1 // 快线下穿慢线
|
||||
midCrossSlowDown := midCrossSlow == -1 // 中线下穿慢线
|
||||
|
||||
// 生成交易信号
|
||||
buyCondition := fastCrossSlowUp && midCrossSlowUp
|
||||
sellCondition := fastCrossSlowDown && midCrossSlowDown
|
||||
|
||||
// 检查时间间隔是否满足要求
|
||||
currentBarNum := e.BarCount(0)
|
||||
barsSinceLastTrade := currentBarNum - m.LastBarNum
|
||||
timeElapsedOk := m.LastBarNum == -1 || barsSinceLastTrade >= float64(timeInterval)
|
||||
|
||||
// 如果有交易信号且时间间隔满足要求
|
||||
if (buyCondition || sellCondition) && timeElapsedOk {
|
||||
// 先平掉所有现有仓位
|
||||
if len(s.LongOrders) > 0 || len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_all",
|
||||
Dirt: core.OdDirtBoth,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 计算止盈止损价格
|
||||
stopLossDist := atrVal * atrMultiplier
|
||||
takeProfitDist := stopLossDist * 1.67 // 保持5:3的盈亏比
|
||||
|
||||
// 根据信号方向开仓
|
||||
if buyCondition {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: stopLossDist,
|
||||
TakeProfitVal: takeProfitDist,
|
||||
StopLossTag: "stop_loss",
|
||||
TakeProfitTag: "take_profit",
|
||||
})
|
||||
m.LastBarNum = currentBarNum
|
||||
} else if sellCondition {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Sell",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: stopLossDist,
|
||||
TakeProfitVal: takeProfitDist,
|
||||
StopLossTag: "stop_loss",
|
||||
TakeProfitTag: "take_profit",
|
||||
})
|
||||
m.LastBarNum = currentBarNum
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
156
llmjob/4267.go
Normal file
156
llmjob/4267.go
Normal file
@@ -0,0 +1,156 @@
|
||||
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 J4267(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
shortMaLength := pol.DefInt("shortMaLength", 10, core.PNorm(5, 20))
|
||||
maRate := pol.Def("maRate", 1.8, core.PNorm(1.5, 4))
|
||||
rsiOverbought := pol.DefInt("rsiOverbought", 75, core.PNorm(60, 85))
|
||||
rsiOversold := pol.DefInt("rsiOversold", 25, core.PNorm(15, 40))
|
||||
bbMultiplier := pol.Def("bbMultiplier", 2.0, core.PNorm(1.5, 3.0))
|
||||
|
||||
// 计算长期均线周期
|
||||
longMaLength := int(float64(shortMaLength) * maRate)
|
||||
|
||||
// 确定预热数量,取所有指标中最大的周期
|
||||
warmupNum := longMaLength
|
||||
if warmupNum < 20 {
|
||||
warmupNum = 20
|
||||
}
|
||||
if warmupNum < 26 {
|
||||
warmupNum = 26
|
||||
}
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "Multi-Technical-Indicator-Synergistic-Trading-System",
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多一个做多订单
|
||||
EachMaxShort: 1, // 最多一个做空订单
|
||||
RunTimeFrames: []string{"4h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算移动平均线
|
||||
shortMa := ta.SMA(e.Close, shortMaLength)
|
||||
longMa := ta.SMA(e.Close, longMaLength)
|
||||
|
||||
// 计算RSI
|
||||
rsi := ta.RSI(e.Close, 14)
|
||||
|
||||
// 计算MACD
|
||||
macdLine, signalLine := ta.MACD(e.Close, 12, 26, 9)
|
||||
macdHist := macdLine.Sub(signalLine)
|
||||
|
||||
// 计算布林带
|
||||
bbUpper, _, bbLower := ta.BBANDS(e.Close, 20, bbMultiplier, bbMultiplier)
|
||||
|
||||
// 获取当前价格
|
||||
closePrice := e.Close.Get(0)
|
||||
if math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测主要交易信号:MA交叉
|
||||
maCross := shortMa.Cross(longMa)
|
||||
buySignal := maCross == 1 // 短期均线上穿长期均线
|
||||
sellSignal := maCross == -1 // 短期均线下穿长期均线
|
||||
|
||||
// 检测辅助交易信号
|
||||
rsiVal := rsi.Get(0)
|
||||
macdHistVal := macdHist.Get(0)
|
||||
bbLowerVal := bbLower.Get(0)
|
||||
bbUpperVal := bbUpper.Get(0)
|
||||
|
||||
additionalBuySignal := !math.IsNaN(rsiVal) && !math.IsNaN(macdHistVal) && !math.IsNaN(bbLowerVal) &&
|
||||
rsiVal < float64(rsiOversold) && macdHistVal > 0 && closePrice < bbLowerVal
|
||||
|
||||
additionalSellSignal := !math.IsNaN(rsiVal) && !math.IsNaN(macdHistVal) && !math.IsNaN(bbUpperVal) &&
|
||||
rsiVal > float64(rsiOverbought) && macdHistVal < 0 && closePrice > bbUpperVal
|
||||
|
||||
// 执行交易策略
|
||||
if buySignal {
|
||||
// 平掉所有做空订单
|
||||
if len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "MA_Sell_Close",
|
||||
Dirt: core.OdDirtShort,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 开做多订单
|
||||
if len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "MA_Buy",
|
||||
})
|
||||
}
|
||||
} else if sellSignal {
|
||||
// 平掉所有做多订单
|
||||
if len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "MA_Buy_Close",
|
||||
Dirt: core.OdDirtLong,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 平掉所有做空订单
|
||||
if len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "MA_Sell_Close",
|
||||
Dirt: core.OdDirtShort,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 开做空订单
|
||||
if len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "MA_Sell",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助信号处理(仅在主要信号未触发时执行)
|
||||
if additionalBuySignal && !buySignal && len(s.LongOrders) == 0 {
|
||||
// 平掉所有做空订单
|
||||
if len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Aux_Sell_Close",
|
||||
Dirt: core.OdDirtShort,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 开做多订单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Aux_Buy",
|
||||
})
|
||||
} else if additionalSellSignal && !sellSignal && len(s.ShortOrders) == 0 {
|
||||
// 平掉所有做多订单
|
||||
if len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Aux_Buy_Close",
|
||||
Dirt: core.OdDirtLong,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 开做空订单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Aux_Sell",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
115
llmjob/4316.go
Normal file
115
llmjob/4316.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// 修复后的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"
|
||||
)
|
||||
|
||||
func J4316(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
ema1Length := pol.DefInt("ema1_length", 19, core.PNorm(5, 50))
|
||||
ema2Length := pol.DefInt("ema2_length", 50, core.PNorm(20, 100))
|
||||
rsiOverbought := pol.DefInt("rsi_overbought", 70, core.PNorm(60, 80))
|
||||
rsiOversold := pol.DefInt("rsi_oversold", 30, core.PNorm(20, 40))
|
||||
macdFast := pol.DefInt("macd_fast", 12, core.PNorm(8, 16))
|
||||
macdSlow := pol.DefInt("macd_slow", 26, core.PNorm(20, 32))
|
||||
macdSignal := pol.DefInt("macd_signal", 9, core.PNorm(6, 12))
|
||||
atrLength := pol.DefInt("atr_length", 14, core.PNorm(7, 21))
|
||||
atrMult := pol.Def("atr_mult", 1.85, core.PNorm(1.5, 5))
|
||||
useVolume := pol.Def("use_volume", 1.0, core.PNorm(0.0, 1.0)) > 0.5 // 修复:使用Def代替DefBool
|
||||
volumeMult := pol.Def("volume_mult", 2.0, core.PNorm(1.0, 4.0))
|
||||
capitalRisk := pol.Def("capital_risk", 2.0, core.PNorm(0.5, 5.0)) / 100
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: ema2Length + 20, // 确保所有指标都有足够数据
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多一个多头订单
|
||||
EachMaxShort: 1, // 最多一个空头订单
|
||||
DrawDownExit: true, // 启用回撤止盈
|
||||
RunTimeFrames: []string{"15m"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算技术指标
|
||||
ema1 := ta.EMA(e.Close, ema1Length)
|
||||
ema2 := ta.EMA(e.Close, ema2Length)
|
||||
rsi := ta.RSI(e.Close, 14)
|
||||
|
||||
// 计算MACD
|
||||
macdLine := ta.EMA(e.Close, macdFast).Sub(ta.EMA(e.Close, macdSlow))
|
||||
macdSignalLine := ta.EMA(macdLine, macdSignal)
|
||||
macdHist := macdLine.Sub(macdSignalLine)
|
||||
|
||||
// 计算ATR
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, atrLength)
|
||||
|
||||
// 计算成交量过滤条件
|
||||
volumeMA := ta.SMA(e.Volume, 20)
|
||||
volumeFilter := e.Volume.Get(0) > volumeMA.Get(0)*volumeMult
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(ema1.Get(0)) || math.IsNaN(ema2.Get(0)) ||
|
||||
math.IsNaN(rsi.Get(0)) || math.IsNaN(macdHist.Get(0)) ||
|
||||
math.IsNaN(atr.Get(0)) {
|
||||
return
|
||||
}
|
||||
|
||||
// 信号过滤 - 优化重复计算
|
||||
crossResult := ema1.Cross(ema2)
|
||||
emaCrossUp := crossResult == 1 // 快速EMA上穿慢速EMA
|
||||
emaCrossDown := crossResult == -1 // 快速EMA下穿慢速EMA
|
||||
rsiFilterLong := rsi.Get(0) > float64(rsiOversold) // RSI高于超卖水平
|
||||
rsiFilterShort := rsi.Get(0) < float64(rsiOverbought) // RSI低于超买水平
|
||||
macdFilterLong := macdHist.Get(0) > 0 // MACD柱状图为正
|
||||
macdFilterShort := macdHist.Get(0) < 0 // MACD柱状图为负
|
||||
volumeOk := !useVolume || volumeFilter // 成交量条件满足
|
||||
|
||||
// 生成交易信号
|
||||
longEntry := emaCrossUp && rsiFilterLong && macdFilterLong && volumeOk
|
||||
shortEntry := emaCrossDown && rsiFilterShort && macdFilterShort && volumeOk
|
||||
|
||||
// 风险管理计算
|
||||
atrValue := atr.Get(0) // 修复:删除未使用的closePrice变量
|
||||
|
||||
// 计算交易规模 - 修复基于ATR的交易规模计算
|
||||
riskAmount := s.Strat.GetStakeAmount(s) * capitalRisk
|
||||
tradeSize := riskAmount / math.Max(atrValue*atrMult, 1.0)
|
||||
|
||||
// 计算追踪止损价格
|
||||
|
||||
// 执行交易
|
||||
if longEntry && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Amount: tradeSize,
|
||||
// 修复:移除不存在的TrailOffset字段
|
||||
})
|
||||
}
|
||||
|
||||
if shortEntry && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
Amount: tradeSize,
|
||||
// 修复:移除不存在的TrailOffset字段
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 实现回撤止盈逻辑
|
||||
GetDrawDownExitRate: func(s *strat.StratJob, od *ormo.InOutOrder, maxChg float64) float64 {
|
||||
// 根据最大盈利回撤比例决定止盈
|
||||
if maxChg > 0.1 { // 盈利超过10%
|
||||
return 0.5 // 回撤50%止盈
|
||||
} else if maxChg > 0.05 { // 盈利超过5%
|
||||
return 0.7 // 回撤70%止盈
|
||||
}
|
||||
return 0 // 不设置回撤止盈
|
||||
},
|
||||
}
|
||||
}
|
||||
268
llmjob/4513.go
Normal file
268
llmjob/4513.go
Normal file
@@ -0,0 +1,268 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
)
|
||||
|
||||
// State4513 存储每个品种的策略状态
|
||||
type State4513 struct {
|
||||
dir int // 当前趋势方向,1=上升趋势,-1=下降趋势
|
||||
longStop float64 // 多头止损价格
|
||||
shortStop float64 // 空头止损价格
|
||||
longStopPrev float64 // 前一个多头止损价格
|
||||
shortStopPrev float64 // 前一个空头止损价格
|
||||
trailingStop float64 // 当前跟踪止损价格
|
||||
}
|
||||
|
||||
// Demo 创建并返回策略实例
|
||||
func J4513(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
bbLength := pol.DefInt("BBLength", 100, core.PNorm(20, 200))
|
||||
stdDev := pol.Def("StdDev", 10, core.PNorm(1, 20))
|
||||
useMovingAverage := pol.Def("useMovingAverage", 1, core.PUniform(0, 1)) // 1=true, 0=false
|
||||
maType := pol.DefInt("maType", 3, core.PUniform(0, 5)) // 0=ema, 1=sma, 2=hma, 3=rma, 4=vwma, 5=wma
|
||||
lookbackPeriod := pol.DefInt("lookbackPeriod", 22, core.PNorm(5, 50))
|
||||
atrMaType := pol.DefInt("atrMaType", 2, core.PUniform(0, 5)) // 0=ema, 1=sma, 2=hma, 3=rma, 4=vwma, 5=wma
|
||||
atrLength := pol.DefInt("atrLength", 10, core.PNorm(5, 30))
|
||||
atrMult := pol.Def("atrMult", 4.2, core.PNorm(1, 10))
|
||||
wicks := pol.Def("wicks", 0, core.PUniform(0, 1)) // 1=true, 0=false
|
||||
considerYearlyHighLow := pol.Def("considerYearlyHighLow", 0, core.PUniform(0, 1)) // 1=true, 0=false
|
||||
considerNewLongTermHighLows := pol.Def("considerNewLongTermHighLows", 0, core.PUniform(0, 1)) // 1=true, 0=false
|
||||
tradeDirection := pol.DefInt("tradeDirection", 0, core.PUniform(0, 2)) // 0=all, 1=long, 2=short
|
||||
|
||||
// 固定参数
|
||||
shortHighLowPeriod := 100
|
||||
longHighLowPeriod := 200
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "Bollinger Percentage Bands",
|
||||
WarmupNum: int(math.Max(float64(bbLength), float64(longHighLowPeriod))) + 10,
|
||||
HedgeOff: true, // 单向持仓
|
||||
EachMaxLong: 1,
|
||||
EachMaxShort: 1,
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化策略状态
|
||||
state := &State4513{
|
||||
dir: 1,
|
||||
longStop: 0,
|
||||
shortStop: 0,
|
||||
}
|
||||
s.More = state
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*State4513)
|
||||
|
||||
// 1. 计算布林带指标
|
||||
// 修复错误1:布林带计算参数错误
|
||||
// 原始策略使用bb(close, BBLength, StdDev),其中StdDev是标准差倍数
|
||||
// 修改为使用正确的参数格式,提供上下轨的标准差倍数
|
||||
upperClose, _, lowerClose := ta.BBANDS(e.Close, bbLength, stdDev, stdDev)
|
||||
upperHigh, _, lowerHigh := ta.BBANDS(e.High, bbLength, stdDev, stdDev)
|
||||
upperLow, _, lowerLow := ta.BBANDS(e.Low, bbLength, stdDev, stdDev)
|
||||
|
||||
// 2. 计算布林带百分比
|
||||
percentBClose := e.Close.Sub(lowerClose).Div(upperClose.Sub(lowerClose)).Mul(100)
|
||||
percentBHigh := e.High.Sub(lowerHigh).Div(upperHigh.Sub(lowerHigh)).Mul(100)
|
||||
percentBLow := e.Low.Sub(lowerLow).Div(upperLow.Sub(lowerLow)).Mul(100)
|
||||
|
||||
// 3. 应用移动平均线平滑
|
||||
var newClose, newHigh, newLow *ta.Series
|
||||
if useMovingAverage > 0.5 {
|
||||
percentBMAClose := getMovingAverage(percentBClose, maType, lookbackPeriod)
|
||||
percentBMAHigh := getMovingAverage(percentBHigh, maType, lookbackPeriod)
|
||||
percentBMALow := getMovingAverage(percentBLow, maType, lookbackPeriod)
|
||||
newClose = percentBMAClose
|
||||
newHigh = percentBMAHigh
|
||||
newLow = percentBMALow
|
||||
} else {
|
||||
newClose = percentBClose
|
||||
newHigh = percentBHigh
|
||||
newLow = percentBLow
|
||||
}
|
||||
|
||||
// 4. 计算ATR和止损
|
||||
closePrev := newClose.Back(1)
|
||||
highPrev := newHigh.Back(1)
|
||||
lowPrev := newLow.Back(1)
|
||||
|
||||
trueRange := newHigh.Max(closePrev).Sub(newLow.Min(closePrev))
|
||||
averageTrueRange := getMovingAverage(trueRange, atrMaType, atrLength)
|
||||
atr := averageTrueRange.Mul(atrMult)
|
||||
|
||||
longStopVal := newClose.Sub(atr)
|
||||
shortStopVal := newClose.Add(atr)
|
||||
|
||||
// 修复错误2:止损更新逻辑不完整
|
||||
// 原始策略使用nz(longStop[1], longStop)处理前一个止损值不存在的情况
|
||||
// 修改为正确处理初始状态,并确保止损只能向有利方向移动
|
||||
if state.longStopPrev == 0 {
|
||||
state.longStopPrev = longStopVal.Get(0)
|
||||
}
|
||||
|
||||
if state.shortStopPrev == 0 {
|
||||
state.shortStopPrev = shortStopVal.Get(0)
|
||||
}
|
||||
|
||||
// 修复错误3:使用Series运算创建常量而不是ta.Const
|
||||
if wicks > 0.5 {
|
||||
if lowPrev.Get(0) > state.longStopPrev {
|
||||
// 使用Max确保止损只能向有利方向移动
|
||||
longStopVal = longStopVal.Max(newClose.Sub(newClose).Add(state.longStopPrev))
|
||||
}
|
||||
} else {
|
||||
if closePrev.Get(0) > state.longStopPrev {
|
||||
longStopVal = longStopVal.Max(newClose.Sub(newClose).Add(state.longStopPrev))
|
||||
}
|
||||
}
|
||||
|
||||
if wicks > 0.5 {
|
||||
if highPrev.Get(0) < state.shortStopPrev {
|
||||
shortStopVal = shortStopVal.Min(newClose.Sub(newClose).Add(state.shortStopPrev))
|
||||
}
|
||||
} else {
|
||||
if closePrev.Get(0) < state.shortStopPrev {
|
||||
shortStopVal = shortStopVal.Min(newClose.Sub(newClose).Add(state.shortStopPrev))
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 判断趋势方向
|
||||
// 修复错误4:趋势方向判断可能存在边界条件处理错误
|
||||
// 原始策略使用三元运算符,逻辑清晰且紧凑
|
||||
// 修改为更接近原始策略的实现
|
||||
if state.dir == -1 {
|
||||
if wicks > 0.5 {
|
||||
if newHigh.Get(0) > state.shortStopPrev {
|
||||
state.dir = 1
|
||||
}
|
||||
} else {
|
||||
if newClose.Get(0) > state.shortStopPrev {
|
||||
state.dir = 1
|
||||
}
|
||||
}
|
||||
} else if state.dir == 1 {
|
||||
if wicks > 0.5 {
|
||||
if newLow.Get(0) < state.longStopPrev {
|
||||
state.dir = -1
|
||||
}
|
||||
} else {
|
||||
if newClose.Get(0) < state.longStopPrev {
|
||||
state.dir = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state.dir == 1 {
|
||||
state.trailingStop = longStopVal.Get(0)
|
||||
} else {
|
||||
state.trailingStop = shortStopVal.Get(0)
|
||||
}
|
||||
|
||||
// 6. 判断辅助条件
|
||||
yearlyHighCondition, yearlyLowCondition := getYearlyHighLowCondition(e, considerYearlyHighLow > 0.5)
|
||||
newHighS, newLowS := calculateNewHighLows(e, shortHighLowPeriod, longHighLowPeriod, considerNewLongTermHighLows > 0.5)
|
||||
|
||||
// 7. 生成交易信号
|
||||
buyCondition := state.dir == 1 && yearlyHighCondition && newHighS
|
||||
exitBuyCondition := state.dir == -1
|
||||
sellCondition := state.dir == -1 && yearlyLowCondition && newLowS
|
||||
exitSellCondition := state.dir == 1
|
||||
|
||||
// 8. 执行交易
|
||||
if buyCondition && (tradeDirection == 0 || tradeDirection == 1) {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
Short: false,
|
||||
})
|
||||
}
|
||||
|
||||
if exitBuyCondition && len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "ExitBuy",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
if sellCondition && (tradeDirection == 0 || tradeDirection == 2) {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Sell",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
if exitSellCondition && len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "ExitSell",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
state.longStop = longStopVal.Get(0)
|
||||
state.shortStop = shortStopVal.Get(0)
|
||||
state.longStopPrev = state.longStop
|
||||
state.shortStopPrev = state.shortStop
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// getMovingAverage 根据类型获取移动平均线
|
||||
func getMovingAverage(source *ta.Series, maType int, length int) *ta.Series {
|
||||
switch maType {
|
||||
case 0: // ema
|
||||
return ta.EMA(source, length)
|
||||
case 1: // sma
|
||||
return ta.SMA(source, length)
|
||||
case 2: // hma
|
||||
return ta.HMA(source, length)
|
||||
case 3: // rma
|
||||
return ta.RMA(source, length)
|
||||
case 4: // vwma - 在Banbot中可能没有直接对应,使用SMA代替
|
||||
return ta.SMA(source, length)
|
||||
case 5: // wma
|
||||
return ta.WMA(source, length)
|
||||
default:
|
||||
return ta.SMA(source, length)
|
||||
}
|
||||
}
|
||||
|
||||
// calculateNewHighLows 计算新的高低点条件
|
||||
func calculateNewHighLows(e *ta.BarEnv, shortPeriod, longPeriod int, consider bool) (bool, bool) {
|
||||
shortHigh := ta.Highest(e.High, shortPeriod)
|
||||
longHigh := ta.Highest(e.High, longPeriod)
|
||||
shortLow := ta.Lowest(e.Low, shortPeriod)
|
||||
longLow := ta.Lowest(e.Low, longPeriod)
|
||||
|
||||
newHigh := true
|
||||
newLow := true
|
||||
|
||||
if consider {
|
||||
newHigh = math.Abs(shortHigh.Get(0)-longHigh.Get(0)) < 1e-10
|
||||
newLow = math.Abs(shortLow.Get(0)-longLow.Get(0)) < 1e-10
|
||||
}
|
||||
|
||||
return newHigh, newLow
|
||||
}
|
||||
|
||||
// getYearlyHighLowCondition 获取年度高低点条件
|
||||
func getYearlyHighLowCondition(e *ta.BarEnv, consider bool) (bool, bool) {
|
||||
if !consider {
|
||||
return true, true
|
||||
}
|
||||
|
||||
// 在Banbot中,我们无法直接获取年度数据,所以简化处理
|
||||
// 使用较长期的高低点来近似年度高低点
|
||||
yearlyHigh := ta.Highest(e.High, 365)
|
||||
yearlyLow := ta.Lowest(e.Low, 365)
|
||||
|
||||
yearlyHighCondition := e.Close.Get(0) > yearlyHigh.Get(0)
|
||||
yearlyLowCondition := e.Close.Get(0) < yearlyLow.Get(0)
|
||||
|
||||
return yearlyHighCondition, yearlyLowCondition
|
||||
}
|
||||
96
llmjob/4583.go
Normal file
96
llmjob/4583.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J4583(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义策略参数
|
||||
rsiLength := pol.DefInt("rsiLength", 5, core.PNorm(2, 14))
|
||||
maLength := pol.DefInt("maLength", 200, core.PNorm(50, 300))
|
||||
exitMaLength := pol.DefInt("exitMaLength", 5, core.PNorm(3, 20))
|
||||
overboughtThreshold := pol.Def("overboughtThreshold", 80, core.PNorm(70, 95))
|
||||
oversoldThreshold := pol.Def("oversoldThreshold", 20, core.PNorm(5, 30))
|
||||
stopLossPercentage := pol.Def("stopLossPercentage", 3, core.PNorm(1, 10))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: maLength, // 预热期设置为长期均线周期
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"1h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算技术指标
|
||||
rsi2 := ta.RSI(e.Close, rsiLength)
|
||||
ma200 := ta.SMA(e.Close, maLength)
|
||||
ma5Exit := ta.SMA(e.Close, exitMaLength)
|
||||
|
||||
// 获取当前指标值
|
||||
rsiValue := rsi2.Get(0)
|
||||
closePrice := e.Close.Get(0)
|
||||
ma200Value := ma200.Get(0)
|
||||
ma5ExitValue := ma5Exit.Get(0)
|
||||
|
||||
// 判断趋势方向
|
||||
positiveTrend := closePrice > ma200Value
|
||||
negativeTrend := closePrice < ma200Value
|
||||
|
||||
// 生成交易信号
|
||||
buyCondition := rsiValue < oversoldThreshold && positiveTrend
|
||||
sellCondition := rsiValue > overboughtThreshold && negativeTrend
|
||||
|
||||
// 生成出场信号
|
||||
exitLongCondition := closePrice > ma5ExitValue
|
||||
exitShortCondition := closePrice < ma5ExitValue
|
||||
|
||||
// 执行交易逻辑
|
||||
if buyCondition && len(s.LongOrders) == 0 {
|
||||
// 开多单,设置止损百分比
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
Short: false,
|
||||
StopLossRate: stopLossPercentage / 100,
|
||||
StopLossTag: "stop_loss",
|
||||
})
|
||||
} else if sellCondition && len(s.ShortOrders) == 0 {
|
||||
// 开空单,设置止损百分比
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Sell",
|
||||
Short: true,
|
||||
StopLossRate: stopLossPercentage / 100,
|
||||
StopLossTag: "stop_loss",
|
||||
})
|
||||
}
|
||||
|
||||
// 计算止损水平
|
||||
var stopLossLevelLong, stopLossLevelShort float64
|
||||
if len(s.LongOrders) > 0 {
|
||||
longEntryPrice := s.LongOrders[0].InitPrice
|
||||
stopLossLevelLong = longEntryPrice * (1 - stopLossPercentage/100)
|
||||
}
|
||||
if len(s.ShortOrders) > 0 {
|
||||
shortEntryPrice := s.ShortOrders[0].InitPrice
|
||||
stopLossLevelShort = shortEntryPrice * (1 + stopLossPercentage/100)
|
||||
}
|
||||
|
||||
// 多头出场条件(包含止损触发)
|
||||
if len(s.LongOrders) > 0 && (exitLongCondition || closePrice <= stopLossLevelLong) {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
// 空头出场条件(包含止损触发)
|
||||
if len(s.ShortOrders) > 0 && (exitShortCondition || closePrice >= stopLossLevelShort) {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
71
llmjob/4670.go
Normal file
71
llmjob/4670.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// 修复后的完整策略代码
|
||||
// 请勿修改package包名和任意函数名
|
||||
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 J4670(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
fastEMAPeriod := pol.DefInt("fastEMAPeriod", 10, core.PNorm(3, 15))
|
||||
emaRate := pol.Def("emaRate", 2.5, core.PNorm(1.5, 5))
|
||||
stopLossPercent := pol.Def("stopLossPercent", 2, core.PNorm(0.5, 5))
|
||||
takeProfitPercent := pol.Def("takeProfitPercent", 4, core.PNorm(1, 10))
|
||||
|
||||
// 计算慢速EMA周期
|
||||
slowEMAPeriod := int(float64(fastEMAPeriod) * emaRate)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowEMAPeriod * 2, // 预热数量设置为慢速EMA周期的2倍
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多同时持有一个多头仓位
|
||||
EachMaxShort: 1, // 最多同时持有一个空头仓位
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算EMA指标
|
||||
ema5 := ta.EMA(e.Close, fastEMAPeriod)
|
||||
ema15 := ta.EMA(e.Close, slowEMAPeriod)
|
||||
|
||||
// 获取当前价格
|
||||
currentPrice := e.Close.Get(0)
|
||||
if math.IsNaN(currentPrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测EMA交叉(只计算一次)
|
||||
crossSignal := ema5.Cross(ema15)
|
||||
longCondition := crossSignal == 1 // 5周期EMA上穿15周期EMA
|
||||
shortCondition := crossSignal == -1 // 5周期EMA下穿15周期EMA
|
||||
|
||||
// 执行交易逻辑
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
// 开多单,使用入场价格计算止损止盈
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
StopLossVal: currentPrice * stopLossPercent / 100,
|
||||
TakeProfitVal: currentPrice * takeProfitPercent / 100,
|
||||
StopLossTag: "Stop Loss",
|
||||
TakeProfitTag: "Take Profit",
|
||||
})
|
||||
}
|
||||
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
// 开空单,使用入场价格计算止损止盈
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
StopLossVal: currentPrice * stopLossPercent / 100,
|
||||
TakeProfitVal: currentPrice * takeProfitPercent / 100,
|
||||
StopLossTag: "Stop Loss",
|
||||
TakeProfitTag: "Take Profit",
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
90
llmjob/4994.go
Normal file
90
llmjob/4994.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J4994(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
rsiLength := pol.DefInt("RSIlength", 14, core.PNorm(5, 50))
|
||||
rsiTop := pol.Def("RSItop", 70.0, core.PNorm(69, 90))
|
||||
rsiBottom := pol.Def("RSIbottom", 35.0, core.PNorm(20, 40))
|
||||
rvolLenRatio := pol.Def("rvolLenRatio", 1.0, core.PNorm(0.5, 3.0))
|
||||
rvolThreshold := pol.Def("RVOLthreshold", 1.5, core.PNorm(1.2, 3.0))
|
||||
|
||||
// 计算实际成交量周期
|
||||
rvolLen := int(float64(rsiLength) * rvolLenRatio)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "RelativeVolumeStrategy",
|
||||
WarmupNum: rsiLength + rvolLen + 1,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多1个多头仓位
|
||||
EachMaxShort: 1, // 最多1个空头仓位
|
||||
RunTimeFrames: []string{"4h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
volume := e.Volume.Get(0)
|
||||
|
||||
// 计算RSI指标
|
||||
rsiVal := ta.RSI(e.Close, rsiLength)
|
||||
currentRSI := rsiVal.Get(0)
|
||||
|
||||
// 计算相对成交量
|
||||
avgVolume := ta.SMA(e.Volume, rvolLen)
|
||||
rvolVal := volume / avgVolume.Get(0)
|
||||
|
||||
// 检测RSI交叉信号
|
||||
rsiCrossTop := rsiVal.Cross(rsiTop)
|
||||
rsiCrossBottom := rsiVal.Cross(rsiBottom)
|
||||
|
||||
// 多头条件:RSI上穿阈值且成交量放大
|
||||
longCondition := rsiCrossTop == 1 && rvolVal > rvolThreshold
|
||||
// 空头条件:RSI下穿阈值且成交量放大
|
||||
shortCondition := rsiCrossBottom == -1 && rvolVal > rvolThreshold
|
||||
// 平多条件:RSI回落
|
||||
closeLong := currentRSI < 69
|
||||
// 平空条件:RSI反弹
|
||||
closeShort := currentRSI > 35
|
||||
|
||||
// 执行交易逻辑
|
||||
hasLong := len(s.LongOrders) > 0
|
||||
hasShort := len(s.ShortOrders) > 0
|
||||
|
||||
// 多头开仓
|
||||
if longCondition && !hasLong {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Short: false,
|
||||
})
|
||||
}
|
||||
|
||||
// 多头平仓
|
||||
if closeLong && hasLong {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CloseLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
// 空头开仓
|
||||
if shortCondition && !hasShort {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 空头平仓
|
||||
if closeShort && hasShort {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CloseShort",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
88
llmjob/5115.go
Normal file
88
llmjob/5115.go
Normal file
@@ -0,0 +1,88 @@
|
||||
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 J5115(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastPeriod := pol.DefInt("fastPeriod", 11, core.PNorm(3, 15))
|
||||
maRate := pol.Def("maRate", 4.4, core.PNorm(2.0, 8.0))
|
||||
atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 30))
|
||||
slMultiplier := pol.Def("slMultiplier", 1.3, core.PNorm(1.0, 5.0))
|
||||
|
||||
// 计算长期均线周期
|
||||
longPeriod := int(float64(fastPeriod) * maRate)
|
||||
|
||||
// 确保预热足够长以覆盖所有指标
|
||||
warmup := longPeriod + atrPeriod + 50
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "Moving-Average-Convergence-Momentum-Cloud-Strategy",
|
||||
WarmupNum: warmup,
|
||||
HedgeOff: true, // 单向持仓
|
||||
DrawDownExit: false,
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
close := e.Close
|
||||
|
||||
// 计算SMA
|
||||
maShort := ta.SMA(close, fastPeriod)
|
||||
maLong := ta.SMA(close, longPeriod)
|
||||
cross := maShort.Cross(maLong)
|
||||
|
||||
// 计算ATR用于止损
|
||||
atr := ta.ATR(e.High, e.Low, close, atrPeriod)
|
||||
atrVal := atr.Get(0)
|
||||
|
||||
// RSI仅用于展示,不参与信号过滤
|
||||
_ = ta.RSI(close, 14) // 保留调用但不用于条件判断
|
||||
|
||||
currentPrice := close.Get(0)
|
||||
if math.IsNaN(currentPrice) || math.IsNaN(atrVal) {
|
||||
return
|
||||
}
|
||||
|
||||
// 买入信号:SMA上穿(无RSI过滤)
|
||||
if cross == 1 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Buy",
|
||||
StopLossVal: atrVal * slMultiplier,
|
||||
StopLossTag: "atr_sl",
|
||||
})
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "ExitShort",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
|
||||
// 卖出信号:SMA下穿
|
||||
if cross == -1 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "sell",
|
||||
Short: true,
|
||||
StopLossVal: atrVal * slMultiplier,
|
||||
StopLossTag: "atr_sl",
|
||||
})
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exitLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
// 可选:计算并保留其他指标用于分析或未来扩展
|
||||
// bbU, bbM, bbL := ta.BBANDS(close, 20, 2, 2)
|
||||
// tenkan := ta.SMA(ta.HL2(e.High, e.Low), 9)
|
||||
// kijun := ta.SMA(ta.HL2(e.High, e.Low), 26)
|
||||
// senkouA := ta.SMA(ta.Add(tenkan, kijun).Div(2), 26)
|
||||
// senkouB := ta.SMA(ta.HL2(e.High, e.Low), 52)
|
||||
// momentum := close.Sub(close.Back(1))
|
||||
// roc := close.Sub(close.Back(12)).Div(close.Back(12)).Mul(100)
|
||||
},
|
||||
}
|
||||
}
|
||||
249
llmjob/5129.go
Normal file
249
llmjob/5129.go
Normal file
@@ -0,0 +1,249 @@
|
||||
// 修复后的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"})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
64
llmjob/559.go
Normal file
64
llmjob/559.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// 修复后的banbot完整策略代码
|
||||
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 J559(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
fastPeriod := pol.DefInt("fastPeriod", 14, core.PNorm(5, 20))
|
||||
macdRate := pol.Def("macdRate", 2.35, core.PNorm(1.5, 4))
|
||||
signalPeriod := 9 // 固定值,不在可调参数中
|
||||
|
||||
// 计算慢线周期
|
||||
slowPeriod := int(float64(fastPeriod) * macdRate)
|
||||
|
||||
// 预热数量应该大于慢线周期
|
||||
warmupNum := slowPeriod + 10
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多一个多头订单
|
||||
EachMaxShort: 1, // 最多一个空头订单
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算MACD指标
|
||||
fastLine, slowLine := ta.MACD(e.Close, fastPeriod, slowPeriod, signalPeriod)
|
||||
|
||||
// 获取当前和前一个周期的值
|
||||
fastCurr := fastLine.Get(0)
|
||||
fastPrev := fastLine.Get(1)
|
||||
slowCurr := slowLine.Get(0)
|
||||
slowPrev := slowLine.Get(1)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(fastCurr) || math.IsNaN(fastPrev) || math.IsNaN(slowCurr) || math.IsNaN(slowPrev) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测MACD快线上穿慢线(金叉)
|
||||
if fastCurr > slowCurr && fastPrev < slowPrev && len(s.LongOrders) == 0 {
|
||||
// 执行买入操作
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Enter Long",
|
||||
})
|
||||
}
|
||||
|
||||
// 检测MACD快线下穿慢线(死叉)
|
||||
if fastCurr < slowCurr && fastPrev > slowPrev && len(s.ShortOrders) == 0 {
|
||||
// 执行卖出操作
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Enter Short",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
215
llmjob/5609.go
Normal file
215
llmjob/5609.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
)
|
||||
|
||||
// State5609 存储每个品种的策略状态
|
||||
type State5609 struct {
|
||||
longEntryPrice float64
|
||||
shortEntryPrice float64
|
||||
longEntryBar int64
|
||||
shortEntryBar int64
|
||||
}
|
||||
|
||||
func J5609(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
atrPeriod := pol.DefInt("atrPeriod", 14, core.PNorm(7, 30))
|
||||
bbLength := pol.DefInt("bbLength", 18, core.PNorm(10, 50))
|
||||
bbStdDev := pol.Def("bbStdDev", 1.9, core.PNorm(1, 3))
|
||||
rsiPeriod := pol.DefInt("rsiPeriod", 12, core.PNorm(7, 21))
|
||||
atrMultiplier := pol.Def("atrMultiplier", 1.6, core.PNorm(0.5, 3))
|
||||
vwapLength := pol.DefInt("vwapLength", 20, core.PNorm(10, 40))
|
||||
targetMultiplier := pol.Def("targetMultiplier", 2.4, core.PNorm(1, 4))
|
||||
maxHoldingBars := pol.DefInt("maxHoldingBars", 3, core.PNorm(2, 10))
|
||||
|
||||
// 计算预热期,确保所有指标都有足够的数据计算
|
||||
warmupNum := bbLength
|
||||
if atrPeriod > warmupNum {
|
||||
warmupNum = atrPeriod
|
||||
}
|
||||
if rsiPeriod > warmupNum {
|
||||
warmupNum = rsiPeriod
|
||||
}
|
||||
if vwapLength > warmupNum {
|
||||
warmupNum = vwapLength
|
||||
}
|
||||
// 额外增加20个K线确保稳定性
|
||||
warmupNum += 20
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "OptionsStrategy",
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多一个多头订单
|
||||
EachMaxShort: 1, // 最多一个空头订单
|
||||
RunTimeFrames: []string{"4h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的策略状态
|
||||
s.More = &State5609{
|
||||
longEntryPrice: 0,
|
||||
shortEntryPrice: 0,
|
||||
longEntryBar: 0,
|
||||
shortEntryBar: 0,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state := s.More.(*State5609)
|
||||
|
||||
// 计算技术指标
|
||||
atrValue := ta.ATR(e.High, e.Low, e.Close, atrPeriod)
|
||||
smaValue := ta.SMA(e.Close, bbLength)
|
||||
stdDev := ta.StdDev(e.Close, bbLength)
|
||||
upperBB := smaValue.Add(stdDev.Mul(bbStdDev))
|
||||
lowerBB := smaValue.Sub(stdDev.Mul(bbStdDev))
|
||||
rsiValue := ta.RSI(e.Close, rsiPeriod)
|
||||
// 修复错误1:使用正确的VWAP计算方法
|
||||
vwap := ta.Sum(e.Close.Mul(e.Volume), vwapLength).Div(ta.Sum(e.Volume, vwapLength))
|
||||
volSMA := ta.SMA(e.Volume, 10)
|
||||
atrSMA := ta.SMA(atrValue, 20)
|
||||
|
||||
// 获取当前值
|
||||
closePrice := e.Close.Get(0)
|
||||
atrVal := atrValue.Get(0)
|
||||
upperBBVal := upperBB.Get(0)
|
||||
lowerBBVal := lowerBB.Get(0)
|
||||
rsiVal := rsiValue.Get(0)
|
||||
vwapVal := vwap.Get(0)
|
||||
volumeVal := e.Volume.Get(0)
|
||||
volSMAVal := volSMA.Get(0)
|
||||
atrSMAVal := atrSMA.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(closePrice) || math.IsNaN(atrVal) || math.IsNaN(upperBBVal) ||
|
||||
math.IsNaN(lowerBBVal) || math.IsNaN(rsiVal) || math.IsNaN(vwapVal) ||
|
||||
math.IsNaN(volumeVal) || math.IsNaN(volSMAVal) || math.IsNaN(atrSMAVal) {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算条件
|
||||
volSpike := volumeVal > volSMAVal*1.5
|
||||
atrFilter := atrVal > atrSMAVal*0.5
|
||||
|
||||
// 生成交易信号
|
||||
longCE := e.Close.Cross(upperBB) == 1 && rsiVal > 60 && volSpike && closePrice > vwapVal && atrFilter
|
||||
longPE := e.Close.Cross(lowerBB) == -1 && rsiVal < 40 && volSpike && closePrice < vwapVal && atrFilter
|
||||
|
||||
// 计算止损止盈
|
||||
longStopLoss := 0.0
|
||||
longTarget := 0.0
|
||||
shortStopLoss := 0.0
|
||||
shortTarget := 0.0
|
||||
|
||||
// 修复错误5:使用实际成交的平均价格作为止损止盈的计算基准
|
||||
if len(s.LongOrders) > 0 {
|
||||
// 获取多头持仓的平均价格
|
||||
longAvgPrice := state.longEntryPrice
|
||||
if longAvgPrice > 0 {
|
||||
longStopLoss = longAvgPrice - atrMultiplier*atrVal
|
||||
longTarget = longAvgPrice + targetMultiplier*atrVal
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.ShortOrders) > 0 {
|
||||
// 获取空头持仓的平均价格
|
||||
shortAvgPrice := state.shortEntryPrice
|
||||
if shortAvgPrice > 0 {
|
||||
shortStopLoss = shortAvgPrice + atrMultiplier*atrVal
|
||||
shortTarget = shortAvgPrice - targetMultiplier*atrVal
|
||||
}
|
||||
}
|
||||
|
||||
// 执行交易
|
||||
if longCE && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "CE Entry",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: atrMultiplier * atrVal,
|
||||
TakeProfitVal: targetMultiplier * atrVal,
|
||||
})
|
||||
// 记录入场价格和K线时间
|
||||
state.longEntryPrice = closePrice
|
||||
state.longEntryBar = e.TimeStop
|
||||
}
|
||||
|
||||
if longPE && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "PE Entry",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: atrMultiplier * atrVal,
|
||||
TakeProfitVal: targetMultiplier * atrVal,
|
||||
})
|
||||
// 记录入场价格和K线时间
|
||||
state.shortEntryPrice = closePrice
|
||||
state.shortEntryBar = e.TimeStop
|
||||
}
|
||||
|
||||
// 出场管理
|
||||
if len(s.LongOrders) > 0 {
|
||||
// 计算持仓时间(K线数)
|
||||
holdBars := e.BarCount(state.longEntryBar)
|
||||
|
||||
// 止盈出场
|
||||
if closePrice >= longTarget {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CE Target Hit",
|
||||
Dirt: core.OdDirtLong,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
// 止损出场
|
||||
if closePrice <= longStopLoss {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CE Stop Loss Hit",
|
||||
Dirt: core.OdDirtLong,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
// 时间出场
|
||||
if int64(holdBars) >= int64(maxHoldingBars) {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CE Timed Exit",
|
||||
Dirt: core.OdDirtLong,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.ShortOrders) > 0 {
|
||||
// 计算持仓时间(K线数)
|
||||
holdBars := e.BarCount(state.shortEntryBar)
|
||||
|
||||
// 止盈出场
|
||||
if closePrice <= shortTarget {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "PE Target Hit",
|
||||
Dirt: core.OdDirtShort,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
// 止损出场
|
||||
if closePrice >= shortStopLoss {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "PE Stop Loss Hit",
|
||||
Dirt: core.OdDirtShort,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
// 时间出场
|
||||
if int64(holdBars) >= int64(maxHoldingBars) {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "PE Timed Exit",
|
||||
Dirt: core.OdDirtShort,
|
||||
Force: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
204
llmjob/5689.go
Normal file
204
llmjob/5689.go
Normal file
@@ -0,0 +1,204 @@
|
||||
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"
|
||||
)
|
||||
|
||||
/*
|
||||
因止损未检查bug导致表现看起来很好,不可用
|
||||
*/
|
||||
|
||||
type State5689 struct {
|
||||
entryPrice float64
|
||||
hlv3 int // 保存三重均线状态
|
||||
}
|
||||
|
||||
func J5689(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
tpPercent := pol.Def("tpPercent", 0.33, core.PNorm(0.15, 0.5)) // 止盈百分比
|
||||
length := pol.DefInt("length", 18, core.PNorm(10, 50)) // 通道计算周期
|
||||
lengthChop := pol.DefInt("lengthChop", 14, core.PNorm(5, 30)) // CHOP指标周期
|
||||
emaPeriod := pol.DefInt("emaPeriod", 10, core.PNorm(5, 30)) // EMA平滑周期
|
||||
maPeriod := pol.DefInt("maPeriod", 50, core.PNorm(30, 100)) // 主均线周期
|
||||
channelRate := pol.Def("channelRate", 0.15, core.PNorm(0.1, 0.5)) // 通道倍率
|
||||
exitPeriod := pol.DefInt("exitPeriod", 12, core.PNorm(5, 30)) // 退出均线周期
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: maPeriod, // 预热K线数量,至少需要主均线周期
|
||||
RunTimeFrames: []string{"1h"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的状态变量
|
||||
s.More = &State5689{
|
||||
entryPrice: math.NaN(),
|
||||
hlv3: 0, // 初始状态为0
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*State5689)
|
||||
|
||||
// 计算技术指标
|
||||
upper := ta.Highest(e.High, length)
|
||||
lower := ta.Lowest(e.Low, length)
|
||||
|
||||
// 计算output值:价格在通道中的百分比位置
|
||||
output := e.Close.Sub(upper).Div(upper.Sub(lower)).Mul(100)
|
||||
emaOutput := ta.EMA(output, emaPeriod)
|
||||
|
||||
// 自定义MA函数:双重WMA平滑
|
||||
maFunc := func(src *ta.Series, len int) *ta.Series {
|
||||
wma1 := ta.WMA(src, len/2)
|
||||
wma2 := ta.WMA(src, len)
|
||||
return ta.WMA(wma1.Mul(2).Sub(wma2), int(math.Round(math.Sqrt(float64(len)))))
|
||||
}
|
||||
|
||||
// 计算主均线和通道
|
||||
BBMC := maFunc(e.Close, maPeriod)
|
||||
rangema := ta.EMA(ta.TR(e.High, e.Low, e.Close), maPeriod)
|
||||
upperk := BBMC.Add(rangema.Mul(channelRate))
|
||||
lowerk := BBMC.Sub(rangema.Mul(channelRate))
|
||||
|
||||
// 计算退出均线
|
||||
exitHigh := ta.WMA(e.High, exitPeriod)
|
||||
exitLow := ta.WMA(e.Low, exitPeriod)
|
||||
|
||||
// 计算RSI指标(原始代码中计算但未使用)
|
||||
_ = ta.RSI(e.Close, 14)
|
||||
|
||||
// 计算CHOP指标(虽然不影响交易逻辑,但为了完整性实现)
|
||||
atr1 := ta.ATR(e.High, e.Low, e.Close, 1)
|
||||
sumAtr := ta.Sum(atr1, lengthChop)
|
||||
highestChop := ta.Highest(e.High, lengthChop)
|
||||
lowestChop := ta.Lowest(e.Low, lengthChop)
|
||||
chopRange := highestChop.Sub(lowestChop)
|
||||
// 避免除以零
|
||||
chopRangeValid := chopRange.Get(0) > 0
|
||||
if chopRangeValid {
|
||||
_ = sumAtr.Div(chopRange).Mul(100)
|
||||
} else {
|
||||
_ = ta.EMA(e.Close, 1).Mul(0) // 创建全0序列
|
||||
}
|
||||
|
||||
// 获取当前值
|
||||
closePrice := e.Close.Get(0)
|
||||
upperkVal := upperk.Get(0)
|
||||
lowerkVal := lowerk.Get(0)
|
||||
BBMCVal := BBMC.Get(0)
|
||||
outputVal := output.Get(0)
|
||||
emaOutputVal := emaOutput.Get(0)
|
||||
exitHighVal := exitHigh.Get(0)
|
||||
exitLowVal := exitLow.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(closePrice) || math.IsNaN(BBMCVal) || math.IsNaN(outputVal) || math.IsNaN(emaOutputVal) {
|
||||
return
|
||||
}
|
||||
|
||||
// 判断价格位置和颜色
|
||||
colorBlue := closePrice > upperkVal
|
||||
colorBarFuchsia := closePrice < lowerkVal
|
||||
|
||||
// 更新三重均线状态
|
||||
if closePrice > exitHighVal {
|
||||
state.hlv3 = 1
|
||||
} else if closePrice < exitLowVal {
|
||||
state.hlv3 = -1
|
||||
}
|
||||
// 否则保持上一个状态
|
||||
|
||||
// 计算退出线
|
||||
_ = exitHigh
|
||||
if state.hlv3 < 0 {
|
||||
_ = exitLow
|
||||
}
|
||||
|
||||
// 生成交易信号
|
||||
closeXBBMC := e.Close.Cross(BBMC)
|
||||
longCondition := closeXBBMC == 1 && outputVal > emaOutputVal
|
||||
shortCondition := closeXBBMC == -1 && outputVal < emaOutputVal
|
||||
|
||||
// 执行交易逻辑
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
// 记录入场价格
|
||||
state.entryPrice = closePrice
|
||||
// 开多单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "ENTER-LONG",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
TakeProfit: closePrice * (1 + tpPercent),
|
||||
})
|
||||
}
|
||||
|
||||
// 为已有订单设置动态止损
|
||||
if len(s.LongOrders) > 0 && !math.IsNaN(state.entryPrice) {
|
||||
od := s.LongOrders[0]
|
||||
if od.Status == ormo.InOutStatusFullEnter {
|
||||
// 只有当color_bar为FUCHSIA时才设置止损
|
||||
if colorBarFuchsia && !math.IsNaN(BBMCVal) {
|
||||
// 设置BBMC作为止损价格
|
||||
od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: BBMCVal,
|
||||
Rate: 1.0, // 全部退出
|
||||
Tag: "dynamic_stop_loss",
|
||||
})
|
||||
} else {
|
||||
// 不设置止损
|
||||
od.SetStopLoss(nil)
|
||||
}
|
||||
|
||||
// 更新止盈
|
||||
takeProfitPrice := state.entryPrice * (1 + tpPercent)
|
||||
od.SetTakeProfit(&ormo.ExitTrigger{
|
||||
Price: takeProfitPrice,
|
||||
Rate: 1.0, // 全部退出
|
||||
Tag: "take_profit",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 做空部分(原始代码中被注释,这里也注释掉)
|
||||
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
state.entryPrice = closePrice
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "ENTER-SHORT",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
Short: true,
|
||||
TakeProfit: closePrice * (1 - tpPercent),
|
||||
})
|
||||
}
|
||||
|
||||
if len(s.ShortOrders) > 0 && !math.IsNaN(state.entryPrice) {
|
||||
od := s.ShortOrders[0]
|
||||
if od.Status == ormo.InOutStatusFullEnter {
|
||||
// 只有当color_bar为BLUE时才设置止损
|
||||
if colorBlue && !math.IsNaN(BBMCVal) {
|
||||
// 设置BBMC作为止损价格
|
||||
od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: BBMCVal,
|
||||
Rate: 1.0, // 全部退出
|
||||
Tag: "dynamic_stop_loss_short",
|
||||
})
|
||||
} else {
|
||||
// 不设置止损
|
||||
od.SetStopLoss(nil)
|
||||
}
|
||||
|
||||
// 更新止盈
|
||||
takeProfitPrice := state.entryPrice * (1 - tpPercent)
|
||||
od.SetTakeProfit(&ormo.ExitTrigger{
|
||||
Price: takeProfitPrice,
|
||||
Rate: 1.0, // 全部退出
|
||||
Tag: "take_profit_short",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
104
llmjob/570.go
Normal file
104
llmjob/570.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J570(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
fastLength := pol.DefInt("fastLength", 20, core.PNorm(5, 20))
|
||||
macdRate := pol.Def("macdRate", 3, core.PNorm(1.5, 3.5))
|
||||
MACDLength := pol.DefInt("MACDLength", 12, core.PNorm(5, 15))
|
||||
macdThreshold := pol.Def("macdThreshold", -0.25, core.PNorm(-0.8, -0.1))
|
||||
takeProfitValue := pol.Def("takeProfitValue", 12, core.PNorm(2, 15))
|
||||
|
||||
// 计算慢速EMA周期
|
||||
slowLength := int(float64(fastLength) * macdRate)
|
||||
|
||||
// 确定预热数量,需要足够的K线来计算MACD和检测谷底
|
||||
warmupNum := slowLength + MACDLength + 3
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
TimeFrames: "8h",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算MACD指标
|
||||
fastEMA := ta.EMA(e.Close, fastLength)
|
||||
slowEMA := ta.EMA(e.Close, slowLength)
|
||||
macd := fastEMA.Sub(slowEMA)
|
||||
aMACD := ta.EMA(macd, MACDLength)
|
||||
delta := macd.Sub(aMACD)
|
||||
|
||||
// 获取当前、前一个和前两个时间点的delta值
|
||||
delta0 := delta.Get(0)
|
||||
delta1 := delta.Get(1)
|
||||
delta2 := delta.Get(2)
|
||||
|
||||
// 获取当前MACD值和收盘价
|
||||
macdValue := macd.Get(0)
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(delta0) || math.IsNaN(delta1) || math.IsNaN(delta2) ||
|
||||
math.IsNaN(macdValue) || math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测MACD谷底
|
||||
isValley := delta0 > delta1 && delta1 < delta2
|
||||
|
||||
// 谷底买入信号
|
||||
if isValley && macdValue <= macdThreshold && delta0 < 0 {
|
||||
// 设置止盈价格
|
||||
|
||||
// 生成买入订单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "valley_buy",
|
||||
TakeProfitVal: takeProfitValue,
|
||||
})
|
||||
}
|
||||
|
||||
// 检测delta与0的交叉
|
||||
crossResult := delta.Cross(0)
|
||||
|
||||
// delta上穿0,买入信号
|
||||
if crossResult == 1 {
|
||||
// 获取当前最高价和最低价
|
||||
highPrice := e.High.Get(0)
|
||||
lowPrice := e.Low.Get(0)
|
||||
|
||||
if !math.IsNaN(highPrice) && !math.IsNaN(lowPrice) {
|
||||
// 生成买入订单,使用固定止盈值0.3
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "MacdLE",
|
||||
TakeProfitVal: 0.3,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// delta下穿0,卖出信号
|
||||
if crossResult == -1 {
|
||||
// 获取当前最高价和最低价
|
||||
highPrice := e.High.Get(0)
|
||||
lowPrice := e.Low.Get(0)
|
||||
|
||||
if !math.IsNaN(highPrice) && !math.IsNaN(lowPrice) {
|
||||
// 生成卖出订单,使用固定止盈值0.3
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "MacdSE",
|
||||
Short: true,
|
||||
TakeProfitVal: 0.3,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
100
llmjob/5743.go
Normal file
100
llmjob/5743.go
Normal file
@@ -0,0 +1,100 @@
|
||||
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 J5743(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastPeriod := pol.DefInt("fastPeriod", 12, core.PNorm(5, 20))
|
||||
macdRate := pol.Def("macdRate", 2.17, core.PNorm(1.5, 4))
|
||||
signalPeriod := pol.DefInt("signalPeriod", 9, core.PNorm(5, 15))
|
||||
stopLossBars := pol.DefInt("stopLossBars", 4, core.PNorm(1, 10))
|
||||
riskRewardRatio := pol.Def("riskRewardRatio", 2, core.PNorm(1, 3))
|
||||
direction := pol.DefInt("direction", 2, core.PUniform(0, 2)) // 0: long, 1: short, 2: both
|
||||
|
||||
// 计算慢速周期
|
||||
slowPeriod := int(float64(fastPeriod) * macdRate)
|
||||
|
||||
// 确定预热数量,取所有参数中的最大值
|
||||
warmupNum := fastPeriod
|
||||
if slowPeriod > warmupNum {
|
||||
warmupNum = slowPeriod
|
||||
}
|
||||
if signalPeriod > warmupNum {
|
||||
warmupNum = signalPeriod
|
||||
}
|
||||
if stopLossBars > warmupNum {
|
||||
warmupNum = stopLossBars
|
||||
}
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
EachMaxLong: 1, // 最多一个做多订单
|
||||
EachMaxShort: 1, // 最多一个做空订单
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算MACD指标
|
||||
macdLine, signalLine := ta.MACD(e.Close, fastPeriod, slowPeriod, signalPeriod)
|
||||
|
||||
// 获取当前值
|
||||
macdVal := macdLine.Get(0)
|
||||
signalVal := signalLine.Get(0)
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 计算直方图值
|
||||
histVal := macdVal - signalVal
|
||||
|
||||
// 检测交叉信号
|
||||
macdCross := macdLine.Cross(signalLine)
|
||||
longSignal := macdCross == 1 && histVal > 0 // MACD金叉且直方图为正
|
||||
shortSignal := macdCross == -1 && histVal < 0 // MACD死叉且直方图为负
|
||||
|
||||
// 计算止损价格
|
||||
longStopLoss := ta.Lowest(e.Low, stopLossBars).Get(0)
|
||||
shortStopLoss := ta.Highest(e.High, stopLossBars).Get(0)
|
||||
|
||||
// 检查止损价格有效性
|
||||
if math.IsNaN(longStopLoss) || math.IsNaN(shortStopLoss) {
|
||||
return
|
||||
}
|
||||
|
||||
// 执行做多交易
|
||||
if direction != 1 && longSignal && len(s.LongOrders) == 0 {
|
||||
// 计算止盈价格
|
||||
distance := closePrice - longStopLoss
|
||||
takeProfit := closePrice + distance*riskRewardRatio
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Larga",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLoss: longStopLoss,
|
||||
TakeProfit: takeProfit,
|
||||
})
|
||||
}
|
||||
|
||||
// 执行做空交易
|
||||
if direction != 0 && shortSignal && len(s.ShortOrders) == 0 {
|
||||
// 计算止盈价格
|
||||
distance := shortStopLoss - closePrice
|
||||
takeProfit := closePrice - distance*riskRewardRatio
|
||||
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Corta",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLoss: shortStopLoss,
|
||||
TakeProfit: takeProfit,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
131
llmjob/5763.go
Normal file
131
llmjob/5763.go
Normal file
@@ -0,0 +1,131 @@
|
||||
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 J5763(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
keh := pol.DefInt("keh", 33, core.PNorm(5, 30))
|
||||
tenkanPeriod := pol.DefInt("tenkanPeriod", 23, core.PNorm(5, 20))
|
||||
kijunTenkanRatio := pol.Def("kijunTenkanRatio", 3.3, core.PNorm(2.0, 4.0))
|
||||
senkouBPeriod := pol.DefInt("senkouBPeriod", 75, core.PNorm(30, 100))
|
||||
displacement := pol.DefInt("displacement", 28, core.PNorm(15, 40))
|
||||
|
||||
// 计算基准线周期
|
||||
kijunPeriod := int(float64(tenkanPeriod) * kijunTenkanRatio)
|
||||
|
||||
// 计算预热期数量,考虑最长的周期和位移
|
||||
warmupNum := senkouBPeriod + displacement
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算Hull移动平均线
|
||||
// 当前周期HMA计算
|
||||
halfKeh := int(math.Round(float64(keh) / 2))
|
||||
n2ma := ta.WMA(e.Close, halfKeh).Mul(2)
|
||||
nma := ta.WMA(e.Close, keh)
|
||||
diff := n2ma.Sub(nma)
|
||||
sqn := int(math.Round(math.Sqrt(float64(keh))))
|
||||
n1 := ta.WMA(diff, sqn)
|
||||
|
||||
// 前一周期HMA计算
|
||||
closeBack1 := e.Close.Back(1)
|
||||
n2ma1 := ta.WMA(closeBack1, halfKeh).Mul(2)
|
||||
nma1 := ta.WMA(closeBack1, keh)
|
||||
diff1 := n2ma1.Sub(nma1)
|
||||
sqn1 := int(math.Round(math.Sqrt(float64(keh))))
|
||||
n2 := ta.WMA(diff1, sqn1)
|
||||
|
||||
// 计算一目均衡图组件
|
||||
// 转换线(Tenkan-sen)
|
||||
tenkanSenHigh := ta.Highest(e.High, tenkanPeriod)
|
||||
tenkanSenLow := ta.Lowest(e.Low, tenkanPeriod)
|
||||
tenkanSen := tenkanSenHigh.Add(tenkanSenLow).Div(2)
|
||||
|
||||
// 基准线(Kijun-sen)
|
||||
kijunSenHigh := ta.Highest(e.High, kijunPeriod)
|
||||
kijunSenLow := ta.Lowest(e.Low, kijunPeriod)
|
||||
kijunSen := kijunSenHigh.Add(kijunSenLow).Div(2)
|
||||
|
||||
// 先行带A(Senkou Span A)
|
||||
senkouSpanA := tenkanSen.Add(kijunSen).Div(2)
|
||||
|
||||
// 先行带B(Senkou Span B)
|
||||
senkouSpanBHigh := ta.Highest(e.High, senkouBPeriod)
|
||||
senkouSpanBLow := ta.Lowest(e.Low, senkouBPeriod)
|
||||
senkouSpanB := senkouSpanBHigh.Add(senkouSpanBLow).Div(2)
|
||||
|
||||
// 先行带高点和低点(位移后)
|
||||
senkouSpanABack := senkouSpanA.Back(displacement - 1)
|
||||
senkouSpanBBack := senkouSpanB.Back(displacement - 1)
|
||||
senkouSpanH := senkouSpanABack.Max(senkouSpanBBack)
|
||||
senkouSpanL := senkouSpanABack.Min(senkouSpanBBack)
|
||||
|
||||
// 滞后线(Chikou Span)
|
||||
chikouSpan := e.Close.Back(displacement - 1)
|
||||
|
||||
// 获取当前值
|
||||
n1Val := n1.Get(0)
|
||||
n2Val := n2.Get(0)
|
||||
closeVal := e.Close.Get(0)
|
||||
tenkanSenVal := tenkanSen.Get(0)
|
||||
kijunSenVal := kijunSen.Get(0)
|
||||
senkouSpanHVal := senkouSpanH.Get(0)
|
||||
senkouSpanLVal := senkouSpanL.Get(0)
|
||||
chikouSpanVal := chikouSpan.Get(0)
|
||||
|
||||
// 多头入场条件
|
||||
longCondition := n1Val > n2Val && closeVal > n2Val && closeVal > chikouSpanVal &&
|
||||
closeVal > senkouSpanHVal && (tenkanSenVal >= kijunSenVal || closeVal > kijunSenVal)
|
||||
|
||||
// 空头入场条件
|
||||
shortCondition := n1Val < n2Val && closeVal < n2Val && closeVal < chikouSpanVal &&
|
||||
closeVal < senkouSpanLVal && (tenkanSenVal <= kijunSenVal || closeVal < kijunSenVal)
|
||||
|
||||
// 多头平仓条件
|
||||
closeLongCondition := n1Val < n2Val && (closeVal < n2Val || tenkanSenVal < kijunSenVal ||
|
||||
closeVal < tenkanSenVal || closeVal < kijunSenVal || closeVal < senkouSpanHVal || closeVal < chikouSpanVal)
|
||||
|
||||
// 空头平仓条件
|
||||
closeShortCondition := n1Val > n2Val && (closeVal > n2Val || tenkanSenVal > kijunSenVal ||
|
||||
closeVal > tenkanSenVal || closeVal > kijunSenVal || closeVal > senkouSpanLVal || closeVal > chikouSpanVal)
|
||||
|
||||
// 执行交易逻辑
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
})
|
||||
}
|
||||
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
if closeLongCondition && len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CloseLong",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
if closeShortCondition && len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "CloseShort",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
144
llmjob/587.go
Normal file
144
llmjob/587.go
Normal file
@@ -0,0 +1,144 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// MartingaleState 存储马丁格尔策略的状态
|
||||
type MartingaleState struct {
|
||||
ContractSize float64 // 当前合约数量
|
||||
MartingaleCount int // 马丁格尔计数器
|
||||
LastTradeResult float64 // 上次交易结果
|
||||
}
|
||||
|
||||
func J587(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastLength := pol.DefInt("fastLength", 15, core.PNorm(5, 30))
|
||||
maRate := pol.Def("maRate", 2.0, core.PNorm(1.5, 4.0))
|
||||
maxMartingaleCount := pol.DefInt("maxMartingaleCount", 3, core.PNorm(1, 5))
|
||||
martingaleMultiplier := pol.Def("martingaleMultiplier", 2.0, core.PNorm(1.5, 3.0))
|
||||
|
||||
// 计算慢线周期
|
||||
slowLength := int(float64(fastLength) * maRate)
|
||||
signalSmoothing := 9
|
||||
|
||||
// 止盈止损比例
|
||||
longTakeProfit := 1.015 // 多头止盈1.5%
|
||||
longStopLoss := 0.99 // 多头止损1%
|
||||
shortTakeProfit := 1.01 // 空头止盈1%
|
||||
shortStopLoss := 0.99 // 空头止损1%
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: slowLength + signalSmoothing, // 确保有足够的数据计算MACD
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
RunTimeFrames: []string{"3d"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化马丁格尔状态
|
||||
s.More = &MartingaleState{
|
||||
ContractSize: 0.02,
|
||||
MartingaleCount: 0,
|
||||
LastTradeResult: 0,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*MartingaleState)
|
||||
|
||||
// 计算MACD指标
|
||||
macdLine, signalLine := ta.MACD(e.Close, fastLength, slowLength, signalSmoothing)
|
||||
|
||||
// 检测MACD交叉信号(只计算一次)
|
||||
macdCross := macdLine.Cross(signalLine)
|
||||
longCondition := macdCross == 1 // 上穿
|
||||
shortCondition := macdCross == -1 // 下穿
|
||||
|
||||
// 获取当前价格
|
||||
currentPrice := e.Close.Get(0)
|
||||
if math.IsNaN(currentPrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 多头信号
|
||||
if longCondition && s.Position(1, "") == 0 {
|
||||
// 计算止盈止损价格
|
||||
takeProfitPrice := currentPrice * longTakeProfit
|
||||
stopLossPrice := currentPrice * longStopLoss
|
||||
takeProfitVal := takeProfitPrice - currentPrice
|
||||
stopLossVal := currentPrice - stopLossPrice
|
||||
|
||||
// 开多单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
Short: false,
|
||||
TakeProfitVal: takeProfitVal,
|
||||
StopLossVal: stopLossVal,
|
||||
TakeProfitTag: "long_take_profit",
|
||||
StopLossTag: "long_stop_loss",
|
||||
CostRate: state.ContractSize / 0.02, // 根据合约数量调整开仓金额
|
||||
})
|
||||
|
||||
// 记录当前交易结果
|
||||
state.LastTradeResult = 0
|
||||
}
|
||||
|
||||
// 空头信号
|
||||
if shortCondition && s.Position(-1, "") == 0 {
|
||||
// 计算止盈止损价格
|
||||
takeProfitPrice := currentPrice / shortTakeProfit
|
||||
stopLossPrice := currentPrice / shortStopLoss
|
||||
takeProfitVal := currentPrice - takeProfitPrice
|
||||
stopLossVal := stopLossPrice - currentPrice
|
||||
|
||||
// 开空单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
Short: true,
|
||||
TakeProfitVal: takeProfitVal,
|
||||
StopLossVal: stopLossVal,
|
||||
TakeProfitTag: "short_take_profit",
|
||||
StopLossTag: "short_stop_loss",
|
||||
CostRate: state.ContractSize / 0.02, // 根据合约数量调整开仓金额
|
||||
})
|
||||
|
||||
// 记录当前交易结果
|
||||
state.LastTradeResult = 0
|
||||
}
|
||||
},
|
||||
OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) {
|
||||
state, _ := s.More.(*MartingaleState)
|
||||
|
||||
// 当订单完全平仓时更新马丁格尔状态
|
||||
if chgType == strat.OdChgExitFill && od.Status == ormo.InOutStatusFullExit {
|
||||
// 获取当前交易结果
|
||||
currentProfit := od.ProfitRate
|
||||
|
||||
if currentProfit < state.LastTradeResult {
|
||||
// 上次交易亏损
|
||||
if state.MartingaleCount < maxMartingaleCount {
|
||||
// 增加马丁格尔计数并加倍合约数量
|
||||
state.ContractSize *= martingaleMultiplier
|
||||
state.MartingaleCount++
|
||||
} else {
|
||||
// 达到最大马丁格尔次数,重置
|
||||
state.ContractSize = 0.02
|
||||
state.MartingaleCount = 0
|
||||
}
|
||||
} else {
|
||||
// 盈利或持平,重置马丁格尔状态
|
||||
state.ContractSize = 0.02
|
||||
state.MartingaleCount = 0
|
||||
}
|
||||
|
||||
// 更新交易结果
|
||||
state.LastTradeResult = currentProfit
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
111
llmjob/592.go
Normal file
111
llmjob/592.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// 修复后的完整策略代码
|
||||
// 请勿修改package包名和任意函数名
|
||||
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"
|
||||
)
|
||||
|
||||
// MACD动态趋势量化交易策略
|
||||
func J592(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
fastPeriod := pol.DefInt("fastPeriod", 19, core.PNorm(5, 20))
|
||||
slowRate := pol.Def("slowRate", 2.9, core.PNorm(1.5, 4))
|
||||
signalPeriod := pol.DefInt("signalPeriod", 10, core.PNorm(5, 15))
|
||||
|
||||
// 计算慢线周期
|
||||
slowPeriod := int(float64(fastPeriod) * slowRate)
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: 100, // 预热K线数量
|
||||
EachMaxLong: 1, // 最多开1单做多
|
||||
EachMaxShort: 1, // 最多开1单做空
|
||||
DrawDownExit: true, // 启用回撤止盈
|
||||
RunTimeFrames: []string{"8h"},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算MACD指标
|
||||
// 使用initType=1,符合中国主流使用习惯
|
||||
macdLine, signalLine := ta.MACDBy(e.Close, fastPeriod, slowPeriod, signalPeriod, 1)
|
||||
|
||||
// 检测MACD线与信号线的交叉
|
||||
// 返回值:1表示上穿(金叉),-1表示下穿(死叉),0表示重合或未知
|
||||
cross := macdLine.Cross(signalLine)
|
||||
|
||||
// 计算ATR用于止损止盈
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, 14)
|
||||
atrVal := atr.Get(0)
|
||||
|
||||
// 获取当前价格
|
||||
|
||||
// 获取当前订单数量
|
||||
longOrders := s.LongOrders
|
||||
shortOrders := s.ShortOrders
|
||||
|
||||
// 金叉信号:MACD线上穿信号线,且当前没有做多订单
|
||||
if cross == 1 && len(longOrders) == 0 {
|
||||
// 如果有空单,先平空单
|
||||
if len(shortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
// 开多单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long_cross",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: atrVal * 2, // 2倍ATR止损
|
||||
TakeProfitVal: atrVal * 4, // 4倍ATR止盈
|
||||
StopLossTag: "atr_stop_loss",
|
||||
TakeProfitTag: "atr_take_profit",
|
||||
})
|
||||
}
|
||||
|
||||
// 死叉信号:MACD线下穿信号线,且当前没有做空订单
|
||||
if cross == -1 && len(shortOrders) == 0 {
|
||||
// 如果有多单,先平多单
|
||||
if len(longOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
// 开空单
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short_cross",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
StopLossVal: atrVal * 2, // 2倍ATR止损
|
||||
TakeProfitVal: atrVal * 4, // 4倍ATR止盈
|
||||
StopLossTag: "atr_stop_loss",
|
||||
TakeProfitTag: "atr_take_profit",
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 回撤止盈计算函数
|
||||
GetDrawDownExitRate: func(s *strat.StratJob, od *ormo.InOutOrder, maxChg float64) float64 {
|
||||
// 最大盈利超过5%后,回撤30%止盈
|
||||
if maxChg > 0.05 {
|
||||
return 0.3
|
||||
}
|
||||
// 最大盈利超过2%后,回撤50%止盈
|
||||
if maxChg > 0.02 {
|
||||
return 0.5
|
||||
}
|
||||
// 其他情况不设置回撤止盈
|
||||
return 0
|
||||
},
|
||||
}
|
||||
}
|
||||
299
llmjob/656.go
Normal file
299
llmjob/656.go
Normal file
@@ -0,0 +1,299 @@
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
60
llmjob/6697.go
Normal file
60
llmjob/6697.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J6697(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
periodK := pol.DefInt("periodK", 13, core.PNorm(5, 30))
|
||||
smoothK := pol.DefInt("smoothK", 5, core.PNorm(2, 8))
|
||||
periodD := pol.DefInt("periodD", 2, core.PNorm(2, 5))
|
||||
tpTicks := pol.DefInt("tpTicks", 100, core.PNorm(10, 500)) // 固定tick止盈
|
||||
overboughtLevel := pol.DefInt("overboughtLevel", 80, core.PNorm(70, 90))
|
||||
oversoldLevel := 100 - overboughtLevel // 自动计算超卖水平
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: periodK + smoothK + periodD + 10, // 确保足够的数据计算指标
|
||||
HedgeOff: true, // 启用单向持仓模式
|
||||
EachMaxLong: 100, // 对应pyramiding=100
|
||||
EachMaxShort: 100, // 对应pyramiding=100
|
||||
TimeFrames: "8h",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算KDJ指标,返回[K, D, RSV]
|
||||
k, d, _ := ta.KDJ(e.High, e.Low, e.Close, periodK, smoothK, periodD)
|
||||
|
||||
// 获取最新值
|
||||
kVal := k.Get(0)
|
||||
|
||||
// 计算交叉信号
|
||||
kdCross := k.Cross(d) // 1:上穿, -1:下穿, 0:重合
|
||||
|
||||
// 使用ATR作为动态止盈参考
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, 14)
|
||||
|
||||
// 做多条件:K线上穿D线,且K值未达超买区
|
||||
if kdCross == 1 && kVal < float64(overboughtLevel) {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
OrderType: core.OrderTypeMarket,
|
||||
TakeProfitVal: atr.Get(0) * float64(tpTicks) / 100, // 使用ATR作为动态止盈
|
||||
})
|
||||
}
|
||||
|
||||
// 做空条件:K线下穿D线,且K值未破超卖区
|
||||
if kdCross == -1 && kVal > float64(oversoldLevel) {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
TakeProfitVal: atr.Get(0) * float64(tpTicks) / 100, // 使用ATR作为动态止盈
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
89
llmjob/6969.go
Normal file
89
llmjob/6969.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J6969(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
moveLimit := pol.Def("moveLimit", 69, core.PNorm(50, 90)) // 大幅移动阈值百分比
|
||||
maLength := pol.DefInt("maLength", 150, core.PNorm(50, 300)) // 均线过滤周期
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: maLength + 3, // 预热数量:均线周期 + 3根K线用于形态判断
|
||||
HedgeOff: true, // 修改为单向持仓模式,与原始Pine Script行为一致
|
||||
TimeFrames: "3d",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算均线指标
|
||||
ma := ta.SMA(e.Close, maLength)
|
||||
|
||||
// 判断K线形态
|
||||
// downBar: 当前K线是否为阴线
|
||||
downBar := e.Open.Get(0) > e.Close.Get(0)
|
||||
downBar1 := e.Open.Get(1) > e.Close.Get(1)
|
||||
downBar2 := e.Open.Get(2) > e.Close.Get(2)
|
||||
|
||||
// isThreeDown: 连续三根阴线
|
||||
isThreeDown := downBar && downBar1 && downBar2
|
||||
// isThreeUp: 连续三根阳线
|
||||
isThreeUp := !downBar && !downBar1 && !downBar2
|
||||
|
||||
// 计算大幅移动指标
|
||||
// isBigMoveDown: 当前K线实体占整个波段的百分比超过阈值
|
||||
highLowRange0 := e.High.Get(0) - e.Low.Get(0)
|
||||
isBigMoveDown := ((e.Open.Get(0) - e.Close.Get(0)) / (0.001 + highLowRange0)) > moveLimit/100.0
|
||||
|
||||
// 复用 highLowRange0 变量,避免重复计算
|
||||
isBigMoveUp := ((e.Close.Get(0) - e.Open.Get(0)) / (0.001 + highLowRange0)) > moveLimit/100.0
|
||||
|
||||
// 生成交易信号
|
||||
// isLongBuy: 连续三阴且最后一根是大跌
|
||||
isLongBuy := isThreeDown && isBigMoveDown
|
||||
// isLongExit: 收盘价突破前一根K线的最高价
|
||||
isLongExit := e.Close.Get(0) > e.High.Get(1)
|
||||
|
||||
// isShortBuy: 连续三阳且最后一根是大涨
|
||||
isShortBuy := isThreeUp && isBigMoveUp
|
||||
// isShortExit: 收盘价跌破前一根K线的最低价
|
||||
isShortExit := e.Close.Get(0) < e.Low.Get(1)
|
||||
|
||||
// 执行交易逻辑
|
||||
// 多头入场
|
||||
if isLongBuy {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Entry Long",
|
||||
Short: false,
|
||||
})
|
||||
}
|
||||
|
||||
// 多头出场
|
||||
if isLongExit {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Entry Long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
|
||||
// 空头入场(需要满足价格低于均线的过滤条件)
|
||||
if isShortBuy && e.Close.Get(0) < ma.Get(0) {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Entry Short",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
// 空头出场
|
||||
if isShortExit {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Entry Short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
131
llmjob/7046.go
Normal file
131
llmjob/7046.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func J7046(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
smaPeriod := pol.DefInt("smaPeriod", 22, core.PNorm(10, 50))
|
||||
mult := pol.Def("mult", 1.0, core.PNorm(0.8, 3.0))
|
||||
trailingLossPct := pol.Def("trailingLossPct", 0.075, core.PNorm(0.03, 0.15))
|
||||
trailingPointsPct := pol.Def("trailingPointsPct", 0.05, core.PNorm(0.01, 0.10))
|
||||
trailingOffsetPct := pol.Def("trailingOffsetPct", 0.005, core.PNorm(0.001, 0.02))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "Bollinger Band Breakout",
|
||||
WarmupNum: smaPeriod + 10, // 预热K线数量
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多1个多单
|
||||
EachMaxShort: 1, // 最多1个空单
|
||||
TimeFrames: "8h",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
closePrice := e.Close.Get(0)
|
||||
|
||||
// 检查数据有效性
|
||||
if math.IsNaN(closePrice) {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算布林带指标
|
||||
upper, _, lower := ta.BBANDS(e.Close, smaPeriod, mult, mult)
|
||||
|
||||
// 检测突破信号
|
||||
longSignal := e.Close.Cross(upper) // 上穿上轨,返回1表示上穿
|
||||
shortSignal := e.Close.Cross(lower) // 下穿下轨,返回-1表示下穿
|
||||
|
||||
// 多头入场逻辑
|
||||
if longSignal == 1 && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
Short: false,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
// 空头入场逻辑
|
||||
if shortSignal == -1 && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
Short: true,
|
||||
OrderType: core.OrderTypeMarket,
|
||||
})
|
||||
}
|
||||
|
||||
// 更新追踪止损
|
||||
updateTrailingStop(s, closePrice, trailingLossPct, trailingPointsPct, trailingOffsetPct)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 更新追踪止损的辅助函数
|
||||
func updateTrailingStop(s *strat.StratJob, closePrice, lossPct, pointsPct, offsetPct float64) {
|
||||
// 处理多头仓位的追踪止损
|
||||
for _, order := range s.LongOrders {
|
||||
if order.Status >= 2 { // 已入场
|
||||
entryPrice := order.Enter.Average
|
||||
maxLossPrice := entryPrice * (1 - lossPct) // 最大损失价格
|
||||
activationPrice := entryPrice * (1 + pointsPct) // 激活追踪的价格
|
||||
|
||||
// 获取当前止损信息
|
||||
currentStop := order.GetStopLoss()
|
||||
|
||||
var newStopPrice float64
|
||||
|
||||
// 判断是否已激活追踪止损
|
||||
if closePrice >= activationPrice {
|
||||
// 已激活,使用偏移量计算止损
|
||||
newStopPrice = closePrice * (1 - offsetPct)
|
||||
} else {
|
||||
// 未激活,使用最大损失作为止损
|
||||
newStopPrice = maxLossPrice
|
||||
}
|
||||
|
||||
// 只有当新止损价格更优(更高)时才更新
|
||||
if currentStop == nil || newStopPrice > currentStop.Price {
|
||||
order.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: newStopPrice,
|
||||
Tag: "trailing_stop",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理空头仓位的追踪止损
|
||||
for _, order := range s.ShortOrders {
|
||||
if order.Status >= 2 { // 已入场
|
||||
entryPrice := order.Enter.Average
|
||||
maxLossPrice := entryPrice * (1 + lossPct) // 最大损失价格
|
||||
activationPrice := entryPrice * (1 - pointsPct) // 激活追踪的价格
|
||||
|
||||
// 获取当前止损信息
|
||||
currentStop := order.GetStopLoss()
|
||||
|
||||
var newStopPrice float64
|
||||
|
||||
// 判断是否已激活追踪止损
|
||||
if closePrice <= activationPrice {
|
||||
// 已激活,使用偏移量计算止损
|
||||
newStopPrice = closePrice * (1 + offsetPct)
|
||||
} else {
|
||||
// 未激活,使用最大损失作为止损
|
||||
newStopPrice = maxLossPrice
|
||||
}
|
||||
|
||||
// 只有当新止损价格更优(更低)时才更新
|
||||
if currentStop == nil || newStopPrice < currentStop.Price {
|
||||
order.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: newStopPrice,
|
||||
Tag: "trailing_stop",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
llmjob/7050.go
Normal file
96
llmjob/7050.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
)
|
||||
|
||||
type NinjaState struct {
|
||||
longEntryBar int64
|
||||
shortEntryBar int64
|
||||
}
|
||||
|
||||
func J7050(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
lookbackPeriods := pol.DefInt("lookbackPeriods", 4, core.PUniform(2, 8))
|
||||
entryOffsetRatio := pol.Def("entryOffsetRatio", 0.85, core.PUniform(0.2, 0.9))
|
||||
barsToHold := pol.DefInt("barsToHold", 0, core.PUniform(0, 5))
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: lookbackPeriods + 1,
|
||||
HedgeOff: true,
|
||||
TimeFrames: "4h",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &NinjaState{
|
||||
longEntryBar: -1,
|
||||
shortEntryBar: -1,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state := s.More.(*NinjaState)
|
||||
currentBar := e.TimeStop / e.TFMSecs
|
||||
|
||||
// 修复:在K线开始时,首先检查是否需要平掉上一根K线建的仓位(对应原始策略中的 K 线收盘平仓)
|
||||
if barsToHold == 0 && s.OrderNum > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{Tag: "close_at_bar_close"})
|
||||
}
|
||||
|
||||
// 获取历史K线数据
|
||||
low1 := e.Low.Get(1)
|
||||
low2 := e.Low.Get(2)
|
||||
low3 := e.Low.Get(3)
|
||||
high1 := e.High.Get(1)
|
||||
high2 := e.High.Get(2)
|
||||
high3 := e.High.Get(3)
|
||||
c := e.Close.Get(0)
|
||||
|
||||
// 优化:缓存重复计算
|
||||
range1 := high1 - low1
|
||||
|
||||
// 计算做多条件:连续3根K线最低价递减且当前收盘价 <= low[1]
|
||||
longCondition := low1 > low2 && low2 > low3 && c <= low1
|
||||
if longCondition && len(s.LongOrders) == 0 {
|
||||
// 计算止损触发价:low[1] + (high[1]-low[1]) * entryOffsetRatio
|
||||
stopPrice := low1 + range1*entryOffsetRatio
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "BarUp",
|
||||
Stop: stopPrice,
|
||||
})
|
||||
state.longEntryBar = currentBar
|
||||
}
|
||||
|
||||
// 计算做空条件:连续3根K线最高价递增且当前收盘价 >= high[1]
|
||||
shortCondition := high1 < high2 && high2 < high3 && c >= high1
|
||||
if shortCondition && len(s.ShortOrders) == 0 {
|
||||
// 计算止损触发价:high[1] - (high[1]-low[1]) * entryOffsetRatio
|
||||
stopPrice := high1 - range1*entryOffsetRatio
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "BarDown",
|
||||
Short: true,
|
||||
Stop: stopPrice,
|
||||
})
|
||||
state.shortEntryBar = currentBar
|
||||
}
|
||||
|
||||
// 平仓逻辑:达到持仓K线数限制 (当 barsToHold > 0 时的增强逻辑)
|
||||
if barsToHold > 0 {
|
||||
if len(s.LongOrders) > 0 && state.longEntryBar >= 0 &&
|
||||
currentBar-state.longEntryBar >= int64(barsToHold) {
|
||||
s.CloseOrders(&strat.ExitReq{Tag: "close_long", Dirt: core.OdDirtLong})
|
||||
}
|
||||
if len(s.ShortOrders) > 0 && state.shortEntryBar >= 0 &&
|
||||
currentBar-state.shortEntryBar >= int64(barsToHold) {
|
||||
s.CloseOrders(&strat.ExitReq{Tag: "close_short", Dirt: core.OdDirtShort})
|
||||
}
|
||||
}
|
||||
|
||||
// 清理已平仓的记录
|
||||
if len(s.LongOrders) == 0 {
|
||||
state.longEntryBar = -1
|
||||
}
|
||||
if len(s.ShortOrders) == 0 {
|
||||
state.shortEntryBar = -1
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
99
llmjob/7096.go
Normal file
99
llmjob/7096.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
// fErf 实现Horner方法的误差函数
|
||||
func fErf(x float64) float64 {
|
||||
nT := 1.0 / (1.0 + 0.5*math.Abs(x))
|
||||
nAns := 1.0 - nT*math.Exp(-x*x-1.26551223+
|
||||
nT*(1.00002368+nT*(0.37409196+nT*(0.09678418+
|
||||
nT*(-0.18628806+nT*(0.27886807+nT*(-1.13520398+
|
||||
nT*(1.48851587+nT*(-0.82215223+nT*(0.17087277))))))))))
|
||||
if x >= 0 {
|
||||
return nAns
|
||||
}
|
||||
return -nAns
|
||||
}
|
||||
|
||||
// fPSignal 计算P-Signal指标
|
||||
func fPSignal(series *ta.Series, period int) *ta.Series {
|
||||
res := series.To("_psignal", period)
|
||||
if res.Cached() {
|
||||
return res
|
||||
}
|
||||
|
||||
if series.Len() < period {
|
||||
return res.Append(math.NaN())
|
||||
}
|
||||
|
||||
nStDev := ta.StdDev(series, period)
|
||||
nSma := ta.SMA(series, period)
|
||||
|
||||
stdevVal := nStDev.Get(0)
|
||||
smaVal := nSma.Get(0)
|
||||
|
||||
var rawPSignal float64
|
||||
if stdevVal > 0 {
|
||||
rawPSignal = fErf(smaVal / (stdevVal * math.Sqrt(2)))
|
||||
} else {
|
||||
rawPSignal = math.Copysign(1.0, smaVal)
|
||||
}
|
||||
|
||||
return res.Append(rawPSignal)
|
||||
}
|
||||
|
||||
func J7096(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
nPoints := pol.DefInt("nPoints", 13, core.PUniform(4, 50))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "P-Signal Strategy",
|
||||
WarmupNum: nPoints + 10, // 额外预留空间确保计算准确
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多一个多头仓位
|
||||
TimeFrames: "8h",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
nIntr := nPoints - 1
|
||||
|
||||
// 1. 计算价格变化 (ohlc4的变化量)
|
||||
avgPrice := ta.AvgPrice(e)
|
||||
priceChange := avgPrice.Sub(avgPrice.Back(1))
|
||||
|
||||
// 2. 计算P-Signal原始值
|
||||
rawPSignal := fPSignal(priceChange, nIntr)
|
||||
|
||||
// 3. 计算平滑P-Signal
|
||||
nPSignal := ta.SMA(rawPSignal, nIntr)
|
||||
|
||||
// 检查数据有效性
|
||||
if nPSignal.Len() < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
// 4. 计算P-Signal变化方向
|
||||
currentPSignal := nPSignal.Get(0)
|
||||
prevPSignal := nPSignal.Get(1)
|
||||
ndPSignal := math.Copysign(1.0, currentPSignal-prevPSignal)
|
||||
|
||||
// 5. 生成交易信号
|
||||
if currentPSignal < 0 && ndPSignal > 0 && len(s.LongOrders) == 0 {
|
||||
// 开多条件:nPSignal < 0 and ndPSignal > 0
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
})
|
||||
} else if currentPSignal > 0 && ndPSignal < 0 && len(s.LongOrders) > 0 {
|
||||
// 平多条件:nPSignal > 0 and ndPSignal < 0
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_long",
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
74
llmjob/7593.go
Normal file
74
llmjob/7593.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func J7593(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调参数
|
||||
lengthRSI := pol.DefInt("lengthRSI", 20, core.PNorm(5, 30))
|
||||
rsiEntryThs := pol.DefInt("rsiEntryThs", 72, core.PNorm(60, 85))
|
||||
rsiExitThs := pol.DefInt("rsiExitThs", 50, core.PNorm(45, 65))
|
||||
takeProfitRate := pol.Def("takeProfitRate", 6.0, core.PNorm(1, 15))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "TrendFollowingRSI",
|
||||
WarmupNum: lengthRSI + 10, // 预热足够的K线用于RSI计算
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最多持有一个多单
|
||||
TimeFrames: "8h",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算RSI指标
|
||||
rsi := ta.RSI(e.Close, lengthRSI)
|
||||
currentRSI := rsi.Get(0)
|
||||
|
||||
// 检查是否有有效的RSI值
|
||||
if math.IsNaN(currentRSI) {
|
||||
return
|
||||
}
|
||||
|
||||
// 入场条件:RSI > 70
|
||||
if currentRSI > float64(rsiEntryThs) && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
Short: false,
|
||||
})
|
||||
}
|
||||
|
||||
// 出场条件:RSI < 55
|
||||
if currentRSI < float64(rsiExitThs) && s.Position(core.OdDirtLong, "") > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "rsi_exit",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
|
||||
// 仅对开仓状态的多头订单检查止盈
|
||||
if !od.Short || od.Status != ormo.InOutStatusFullEnter {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 基于持仓成本计算止盈目标价
|
||||
longTakeProfit := od.InitPrice * (1 + takeProfitRate/100)
|
||||
|
||||
// 检查是否达到止盈条件
|
||||
if s.Env.Close.Get(0) > longTakeProfit {
|
||||
return &strat.ExitReq{
|
||||
Tag: "tp_exit",
|
||||
Dirt: core.OdDirtLong,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
624
llmjob/7699.go
Normal file
624
llmjob/7699.go
Normal file
@@ -0,0 +1,624 @@
|
||||
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"
|
||||
)
|
||||
|
||||
type HullBusterState struct {
|
||||
// 交易状态
|
||||
entryPrice float64
|
||||
stopPrice float64
|
||||
lastLevel float64
|
||||
maeLevel float64
|
||||
pyramidCount int
|
||||
pyramidWaveID int
|
||||
entryTrendDir int
|
||||
consecLosses int
|
||||
|
||||
// 交叉跟踪
|
||||
riseALCrossPrice float64
|
||||
riseALNetChg float64
|
||||
fallALCrossPrice float64
|
||||
fallALNetChg float64
|
||||
riseALBarsSince int
|
||||
fallALBarsSince int
|
||||
|
||||
// EMA交叉跟踪
|
||||
riseEMCrossPrice float64
|
||||
riseEMNetChg float64
|
||||
fallEMCrossPrice float64
|
||||
fallEMNetChg float64
|
||||
riseEMBarsSince int
|
||||
fallEMBarsSince int
|
||||
|
||||
// ZScore跟踪
|
||||
riseZSSince int
|
||||
fallZSSince int
|
||||
riseZSCount int
|
||||
fallZSCount int
|
||||
riseZSHigh float64
|
||||
fallZSLow float64
|
||||
aboveZSSince int
|
||||
belowZSSince int
|
||||
aboveZSCount int
|
||||
belowZSCount int
|
||||
|
||||
// 事件状态
|
||||
htfEventHigh float64
|
||||
htfEventLow float64
|
||||
lastHTFEvent int
|
||||
impulseWaveDir int
|
||||
impulseWaveID int
|
||||
|
||||
// 状态标志
|
||||
nascentEntry bool
|
||||
hemiTransit bool
|
||||
feeRise bool
|
||||
feeFall bool
|
||||
pullback bool
|
||||
}
|
||||
|
||||
func J7699(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 可调参数定义
|
||||
fastCrossLen := pol.DefInt("fastCrossLen", 9, core.PNorm(5, 20))
|
||||
smaCrossRate := pol.Def("smaCrossRate", 2.6, core.PNorm(1.5, 5.0))
|
||||
trendDEMALen := pol.DefInt("trendDEMALen", 200, core.PNorm(50, 300))
|
||||
fastAlmaLen := pol.DefInt("fastAlmaLen", 10, core.PNorm(5, 30))
|
||||
almaLinregRate := pol.Def("almaLinregRate", 3.0, core.PNorm(2.0, 7.0))
|
||||
|
||||
// 固定参数
|
||||
slowCrossLen := int(float64(fastCrossLen) * smaCrossRate)
|
||||
slowLinRegLen := int(float64(fastAlmaLen) * almaLinregRate)
|
||||
fastLinRegLen := 30
|
||||
fastEMALen := 8
|
||||
stochastLen := 14
|
||||
minProfit := 0.50
|
||||
stopOffset := 0.50
|
||||
maxAddOns := 0
|
||||
pyramidSpan := 0.0
|
||||
tickScaler := 10.0
|
||||
chgDivisor := 1000.0
|
||||
|
||||
// 计算所需预热长度
|
||||
warmupNum := fastCrossLen
|
||||
if slowCrossLen > warmupNum {
|
||||
warmupNum = slowCrossLen
|
||||
}
|
||||
if trendDEMALen > warmupNum {
|
||||
warmupNum = trendDEMALen
|
||||
}
|
||||
if fastAlmaLen > warmupNum {
|
||||
warmupNum = fastAlmaLen
|
||||
}
|
||||
if slowLinRegLen > warmupNum {
|
||||
warmupNum = slowLinRegLen
|
||||
}
|
||||
if fastLinRegLen > warmupNum {
|
||||
warmupNum = fastLinRegLen
|
||||
}
|
||||
if fastEMALen > warmupNum {
|
||||
warmupNum = fastEMALen
|
||||
}
|
||||
if stochastLen > warmupNum {
|
||||
warmupNum = stochastLen
|
||||
}
|
||||
warmupNum += 100 // 为ZScore等计算预留空间
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmupNum,
|
||||
HedgeOff: true, // 单向持仓
|
||||
TimeFrames: "8h",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &HullBusterState{}
|
||||
},
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
// 需要两个高时间框架:信号周期和趋势周期
|
||||
return []*strat.PairSub{
|
||||
{"_cur_", "1h", warmupNum}, // 信号周期
|
||||
{"_cur_", "4h", warmupNum}, // 趋势周期
|
||||
}
|
||||
},
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair string, tf string) {
|
||||
// 处理高时间框架数据
|
||||
if tf == "1h" {
|
||||
// 将1h数据存储到环境中
|
||||
s.Env.Data.Store("_cur_1h", e)
|
||||
} else if tf == "4h" {
|
||||
// 将4h数据存储到环境中
|
||||
s.Env.Data.Store("_cur_4h", e)
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*HullBusterState)
|
||||
|
||||
// 当前周期指标计算
|
||||
fastCross := ta.SMA(e.Close, fastCrossLen)
|
||||
slowCross := ta.SMA(e.Close, slowCrossLen)
|
||||
fastEMA := ta.EMA(e.Close, fastEMALen)
|
||||
|
||||
// 使用Stochastic指标而非KDJ
|
||||
fastK := Stochastic(e.High, e.Low, e.Close, stochastLen)
|
||||
slowD := ta.SMA(fastK, 3)
|
||||
|
||||
// 交叉信号 - 只计算一次
|
||||
crossValue := fastCross.Cross(slowCross)
|
||||
crossRise := crossValue == 1
|
||||
crossFall := crossValue == -1
|
||||
|
||||
// 从高时间框架获取数据
|
||||
htfSignalData, _ := s.Env.Data.Load("_cur_1h")
|
||||
htfTrendData, _ := s.Env.Data.Load("_cur_4h")
|
||||
|
||||
if htfSignalData == nil || htfTrendData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
signalEnv := htfSignalData.(*ta.BarEnv)
|
||||
trendEnv := htfTrendData.(*ta.BarEnv)
|
||||
|
||||
// HTF指标
|
||||
slowLinReg := ta.LinReg(trendEnv.Close, slowLinRegLen)
|
||||
fastLinReg := ta.LinReg(signalEnv.Close, fastLinRegLen)
|
||||
fastAlma := ta.ALMA(signalEnv.Close, fastAlmaLen, 6.0, 0.85)
|
||||
trendDEMA := DEMA(trendEnv.Close, trendDEMALen)
|
||||
|
||||
// ZScore计算
|
||||
almaMean := ta.SMA(fastAlma, 100)
|
||||
almaMarks := fastAlma.Sub(almaMean)
|
||||
almaStdDev := ta.StdDev(almaMarks, 100)
|
||||
zScore := almaMarks.Div(almaStdDev)
|
||||
|
||||
// HTF Stochastic
|
||||
htfFastK := Stochastic(trendEnv.High, trendEnv.Low, trendEnv.Close, stochastLen)
|
||||
htfDLine := ta.SMA(htfFastK, 3)
|
||||
htfSlowD := ta.SMA(htfDLine, 3)
|
||||
|
||||
// 信号条件 - 使用Get(1)获取前一根K线值以对齐HTF数据
|
||||
closePrice := e.Close.Get(0)
|
||||
trendDEMAVal := trendDEMA.Get(1)
|
||||
fastAlmaVal := fastAlma.Get(1)
|
||||
fastLinRegVal := fastLinReg.Get(1)
|
||||
slowLinRegVal := slowLinReg.Get(1)
|
||||
zScoreVal := zScore.Get(1)
|
||||
|
||||
// 趋势信号
|
||||
htfSignalRise := fastAlmaVal > fastLinRegVal
|
||||
htfSignalFall := fastAlmaVal < fastLinRegVal
|
||||
htfFastAboveTrend := fastAlmaVal > trendDEMAVal && fastLinRegVal > trendDEMAVal
|
||||
htfFastBelowTrend := fastAlmaVal < trendDEMAVal && fastLinRegVal < trendDEMAVal
|
||||
|
||||
// 计算ALMA事件
|
||||
updateALEvents(state, fastAlmaVal, fastLinRegVal, closePrice, tickScaler, chgDivisor)
|
||||
|
||||
// 计算EMA事件
|
||||
fastEMAVal := fastEMA.Get(0)
|
||||
updateEMEvents(state, fastEMAVal, fastAlmaVal, closePrice, tickScaler, chgDivisor)
|
||||
|
||||
// 更新ZScore状态
|
||||
updateZScoreState(state, zScoreVal, closePrice)
|
||||
|
||||
// 更新脉冲波方向
|
||||
waveDirNow := 0
|
||||
if fastLinRegVal > slowLinRegVal {
|
||||
waveDirNow = 1
|
||||
} else if fastLinRegVal < slowLinRegVal {
|
||||
waveDirNow = -1
|
||||
}
|
||||
|
||||
if waveDirNow != state.impulseWaveDir {
|
||||
state.impulseWaveDir = waveDirNow
|
||||
state.impulseWaveID++
|
||||
}
|
||||
|
||||
// 计算事件
|
||||
riseALEvent := checkRiseEvent(state, 10.0, 0.1)
|
||||
fallALEvent := checkFallEvent(state, 10.0, 0.1)
|
||||
riseEMEvent := checkRiseEMEvent(state, 3.0, 0.8)
|
||||
fallEMEvent := checkFallEMEvent(state, 4.0, 0.8)
|
||||
|
||||
// Stochastic事件
|
||||
fullFanCone := fastEMAVal > fastAlmaVal && fastAlmaVal > fastLinRegVal && fastLinRegVal > slowLinRegVal
|
||||
downFanCone := fastEMAVal < fastAlmaVal && fastAlmaVal < fastLinRegVal && fastLinRegVal < slowLinRegVal
|
||||
partialTrendRise := fastEMAVal > fastAlmaVal && fastAlmaVal > slowLinRegVal && fastLinRegVal > slowLinRegVal
|
||||
partialTrendFall := fastEMAVal < fastAlmaVal && fastAlmaVal < slowLinRegVal && fastLinRegVal < slowLinRegVal
|
||||
durableTrendRise := partialTrendRise && slowLinRegVal > trendDEMAVal
|
||||
durableTrendFall := partialTrendFall && slowLinRegVal < trendDEMAVal
|
||||
|
||||
riseStochas := (htfDLine.Get(1) > 80 && fullFanCone) ||
|
||||
(htfSlowD.Get(1) > 90 && durableTrendRise) ||
|
||||
(htfDLine.Get(1) > 90 && partialTrendRise && state.htfEventHigh > 0 && closePrice > state.htfEventHigh)
|
||||
|
||||
fallStochas := (htfDLine.Get(1) < 20 && downFanCone) ||
|
||||
(htfSlowD.Get(1) < 10 && durableTrendFall) ||
|
||||
(htfDLine.Get(1) < 10 && partialTrendFall && state.htfEventLow > 0 && closePrice < state.htfEventLow)
|
||||
|
||||
// 更新HTF事件高低点
|
||||
if riseALEvent {
|
||||
state.htfEventHigh = closePrice
|
||||
state.lastHTFEvent = 1
|
||||
}
|
||||
if fallALEvent {
|
||||
state.htfEventLow = closePrice
|
||||
state.lastHTFEvent = -1
|
||||
}
|
||||
|
||||
// Nascent Entry条件
|
||||
htfNascentRise := fastAlmaVal > slowLinRegVal && fastLinRegVal > slowLinRegVal && slowLinRegVal < trendDEMAVal
|
||||
htfNascentFall := fastAlmaVal < slowLinRegVal && fastLinRegVal < slowLinRegVal && slowLinRegVal > trendDEMAVal
|
||||
|
||||
// 检查是否有持仓
|
||||
positionDir := 0
|
||||
if len(s.LongOrders) > 0 {
|
||||
positionDir = 1
|
||||
} else if len(s.ShortOrders) > 0 {
|
||||
positionDir = -1
|
||||
}
|
||||
|
||||
alreadyIn := positionDir != 0
|
||||
|
||||
// 计算盈利状态
|
||||
var currentOrder *ormo.InOutOrder
|
||||
if len(s.LongOrders) > 0 {
|
||||
currentOrder = s.LongOrders[0]
|
||||
} else if len(s.ShortOrders) > 0 {
|
||||
currentOrder = s.ShortOrders[0]
|
||||
}
|
||||
|
||||
profitable := false
|
||||
if currentOrder != nil {
|
||||
profitAmount := currentOrder.ProfitRate * currentOrder.Leverage * state.entryPrice
|
||||
profitable = profitAmount >= minProfit
|
||||
}
|
||||
|
||||
// 更新ZScore退出条件
|
||||
if alreadyIn {
|
||||
if zScoreVal > 5.0 {
|
||||
state.feeRise = true
|
||||
}
|
||||
if zScoreVal < -5.0 {
|
||||
state.feeFall = true
|
||||
}
|
||||
}
|
||||
|
||||
// 入场逻辑(包含Nascent Entry)
|
||||
if !alreadyIn {
|
||||
if crossRise {
|
||||
longNascent := false
|
||||
if closePrice <= trendDEMAVal && htfNascentRise {
|
||||
longNascent = true
|
||||
state.nascentEntry = true
|
||||
}
|
||||
|
||||
if closePrice > trendDEMAVal || longNascent {
|
||||
stopPrice := e.Low.Get(0) - stopOffset
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "WBHDrive-L",
|
||||
Short: false,
|
||||
StopLoss: stopPrice,
|
||||
StopLossTag: "stop_loss",
|
||||
})
|
||||
state.entryPrice = closePrice
|
||||
state.stopPrice = stopPrice
|
||||
state.lastLevel = closePrice
|
||||
state.entryTrendDir = 1
|
||||
if !longNascent {
|
||||
state.nascentEntry = false
|
||||
}
|
||||
}
|
||||
} else if crossFall {
|
||||
shortNascent := false
|
||||
if closePrice >= trendDEMAVal && htfNascentFall {
|
||||
shortNascent = true
|
||||
state.nascentEntry = true
|
||||
}
|
||||
|
||||
if closePrice < trendDEMAVal || shortNascent {
|
||||
stopPrice := e.High.Get(0) + stopOffset
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "WBHDrive-S",
|
||||
Short: true,
|
||||
StopLoss: stopPrice,
|
||||
StopLossTag: "stop_loss",
|
||||
})
|
||||
state.entryPrice = closePrice
|
||||
state.stopPrice = stopPrice
|
||||
state.lastLevel = closePrice
|
||||
state.entryTrendDir = -1
|
||||
if !shortNascent {
|
||||
state.nascentEntry = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 出场逻辑
|
||||
if alreadyIn && profitable {
|
||||
shouldClose := false
|
||||
|
||||
if positionDir == 1 { // 多头
|
||||
// 交叉退出
|
||||
if htfSignalFall {
|
||||
shouldClose = true
|
||||
}
|
||||
// 信号退出
|
||||
if htfSignalRise && riseALEvent && state.feeRise {
|
||||
shouldClose = true
|
||||
}
|
||||
// 事件退出
|
||||
if riseEMEvent && fastEMAVal > fastAlmaVal && htfFastAboveTrend {
|
||||
shouldClose = true
|
||||
}
|
||||
// Stochastic退出
|
||||
if riseStochas {
|
||||
shouldClose = true
|
||||
}
|
||||
} else if positionDir == -1 { // 空头
|
||||
// 交叉退出
|
||||
if htfSignalRise {
|
||||
shouldClose = true
|
||||
}
|
||||
// 信号退出
|
||||
if htfSignalFall && fallALEvent && state.feeFall {
|
||||
shouldClose = true
|
||||
}
|
||||
// 事件退出
|
||||
if fallEMEvent && fastEMAVal < fastAlmaVal && htfFastBelowTrend {
|
||||
shouldClose = true
|
||||
}
|
||||
// Stochastic退出
|
||||
if fallStochas {
|
||||
shouldClose = true
|
||||
}
|
||||
}
|
||||
|
||||
if shouldClose {
|
||||
if positionDir == 1 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
} else {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
if profitable {
|
||||
state.consecLosses = 0
|
||||
} else {
|
||||
state.consecLosses++
|
||||
}
|
||||
state.entryPrice = 0
|
||||
state.stopPrice = 0
|
||||
state.lastLevel = 0
|
||||
state.pyramidCount = 0
|
||||
state.feeRise = false
|
||||
state.feeFall = false
|
||||
}
|
||||
}
|
||||
|
||||
// 完整的金字塔加仓逻辑
|
||||
if alreadyIn && maxAddOns > 0 && state.consecLosses < maxAddOns {
|
||||
// 检测pullback条件
|
||||
if positionDir == 1 && slowD.Get(0) < 20 {
|
||||
if fallALEvent || fallStochas {
|
||||
state.pullback = true
|
||||
}
|
||||
} else if positionDir == -1 && slowD.Get(0) > 80 {
|
||||
if riseALEvent || riseStochas {
|
||||
state.pullback = true
|
||||
}
|
||||
}
|
||||
|
||||
shouldPyramid := false
|
||||
|
||||
if positionDir == 1 {
|
||||
// 完整的多头金字塔逻辑
|
||||
exceedingSpan := pyramidSpan > 0 && (state.lastLevel-closePrice) > pyramidSpan
|
||||
|
||||
if (crossRise && closePrice > trendDEMAVal && htfFastAboveTrend && closePrice > state.lastLevel && state.pullback) ||
|
||||
(closePrice < state.lastLevel && exceedingSpan && closePrice > state.entryPrice && (fallALEvent || fallEMEvent || fallStochas)) ||
|
||||
(closePrice < state.lastLevel && exceedingSpan && closePrice < state.entryPrice && fallALEvent && fallStochas && state.impulseWaveID > state.pyramidWaveID) {
|
||||
shouldPyramid = true
|
||||
}
|
||||
} else if positionDir == -1 {
|
||||
// 完整的空头金字塔逻辑
|
||||
exceedingSpan := pyramidSpan > 0 && (closePrice-state.lastLevel) > pyramidSpan
|
||||
|
||||
if (crossFall && closePrice < trendDEMAVal && htfFastBelowTrend && closePrice < state.lastLevel && state.pullback) ||
|
||||
(closePrice > state.lastLevel && exceedingSpan && closePrice < state.entryPrice && (riseALEvent || riseEMEvent || riseStochas)) ||
|
||||
(closePrice > state.lastLevel && exceedingSpan && closePrice > state.entryPrice && riseALEvent && riseStochas && state.impulseWaveID > state.pyramidWaveID) {
|
||||
shouldPyramid = true
|
||||
}
|
||||
}
|
||||
|
||||
if shouldPyramid {
|
||||
if positionDir == 1 {
|
||||
stopPrice := e.Low.Get(0) - stopOffset
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "WBHDrive-L-Pyramid",
|
||||
Short: false,
|
||||
StopLoss: stopPrice,
|
||||
StopLossTag: "stop_loss",
|
||||
})
|
||||
} else {
|
||||
stopPrice := e.High.Get(0) + stopOffset
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "WBHDrive-S-Pyramid",
|
||||
Short: true,
|
||||
StopLoss: stopPrice,
|
||||
StopLossTag: "stop_loss",
|
||||
})
|
||||
}
|
||||
state.pyramidCount++
|
||||
state.pyramidWaveID = state.impulseWaveID
|
||||
state.lastLevel = closePrice
|
||||
state.pullback = false
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 实现与PineScript stoch()函数一致的Stochastic指标
|
||||
func Stochastic(high, low, close *ta.Series, length int) *ta.Series {
|
||||
llowest := ta.Lowest(low, length)
|
||||
hhighest := ta.Highest(high, length)
|
||||
k := close.Sub(llowest).Div(hhighest.Sub(llowest)).Mul(100)
|
||||
return k
|
||||
}
|
||||
|
||||
// 辅助函数:更新ALMA事件状态(包含tick标准化)
|
||||
func updateALEvents(state *HullBusterState, fastAlma, fastLinReg, closePrice, tickScaler, chgDivisor float64) {
|
||||
fxMinTick := tickScaler
|
||||
|
||||
if fastAlma > fastLinReg {
|
||||
if state.fallALBarsSince > 0 { // 从下跌转为上涨
|
||||
state.fallALBarsSince = 0
|
||||
state.fallALCrossPrice = 0
|
||||
state.riseALBarsSince = 1
|
||||
state.riseALCrossPrice = closePrice
|
||||
state.riseALNetChg = 0
|
||||
} else {
|
||||
state.riseALBarsSince++
|
||||
if state.riseALCrossPrice > 0 {
|
||||
divisor := chgDivisor
|
||||
if chgDivisor <= 1.0 {
|
||||
divisor = state.riseALCrossPrice
|
||||
}
|
||||
state.riseALNetChg = ((closePrice - state.riseALCrossPrice) / fxMinTick / divisor) * 100.0
|
||||
}
|
||||
}
|
||||
} else if fastAlma < fastLinReg {
|
||||
if state.riseALBarsSince > 0 { // 从上涨转为下跌
|
||||
state.riseALBarsSince = 0
|
||||
state.riseALCrossPrice = 0
|
||||
state.fallALBarsSince = 1
|
||||
state.fallALCrossPrice = closePrice
|
||||
state.fallALNetChg = 0
|
||||
} else {
|
||||
state.fallALBarsSince++
|
||||
if state.fallALCrossPrice > 0 {
|
||||
divisor := chgDivisor
|
||||
if chgDivisor <= 1.0 {
|
||||
divisor = state.fallALCrossPrice
|
||||
}
|
||||
state.fallALNetChg = ((state.fallALCrossPrice - closePrice) / fxMinTick / divisor) * 100.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:更新EMA事件状态(包含tick标准化)
|
||||
func updateEMEvents(state *HullBusterState, fastEMA, fastAlma, closePrice, tickScaler, chgDivisor float64) {
|
||||
fxMinTick := tickScaler
|
||||
|
||||
if fastEMA > fastAlma {
|
||||
if state.fallEMBarsSince > 0 {
|
||||
state.fallEMBarsSince = 0
|
||||
state.fallEMCrossPrice = 0
|
||||
state.riseEMBarsSince = 1
|
||||
state.riseEMCrossPrice = closePrice
|
||||
state.riseEMNetChg = 0
|
||||
} else {
|
||||
state.riseEMBarsSince++
|
||||
if state.riseEMCrossPrice > 0 {
|
||||
divisor := chgDivisor
|
||||
if chgDivisor <= 1.0 {
|
||||
divisor = state.riseEMCrossPrice
|
||||
}
|
||||
state.riseEMNetChg = ((closePrice - state.riseEMCrossPrice) / fxMinTick / divisor) * 100.0
|
||||
}
|
||||
}
|
||||
} else if fastEMA < fastAlma {
|
||||
if state.riseEMBarsSince > 0 {
|
||||
state.riseEMBarsSince = 0
|
||||
state.riseEMCrossPrice = 0
|
||||
state.fallEMBarsSince = 1
|
||||
state.fallEMCrossPrice = closePrice
|
||||
state.fallEMNetChg = 0
|
||||
} else {
|
||||
state.fallEMBarsSince++
|
||||
if state.fallEMCrossPrice > 0 {
|
||||
divisor := chgDivisor
|
||||
if chgDivisor <= 1.0 {
|
||||
divisor = state.fallEMCrossPrice
|
||||
}
|
||||
state.fallEMNetChg = ((state.fallEMCrossPrice - closePrice) / fxMinTick / divisor) * 100.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:更新ZScore状态
|
||||
func updateZScoreState(state *HullBusterState, zScore, closePrice float64) {
|
||||
state.aboveZSSince++
|
||||
state.belowZSSince++
|
||||
state.riseZSSince++
|
||||
state.fallZSSince++
|
||||
|
||||
// 简化的ZScore跟踪
|
||||
if zScore > 0 {
|
||||
if state.aboveZSSince > 0 {
|
||||
state.aboveZSSince = 0
|
||||
state.aboveZSCount = 1
|
||||
} else {
|
||||
state.aboveZSCount++
|
||||
}
|
||||
}
|
||||
|
||||
if zScore < 0 {
|
||||
if state.belowZSSince > 0 {
|
||||
state.belowZSSince = 0
|
||||
state.belowZSCount = 1
|
||||
} else {
|
||||
state.belowZSCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:检查上升事件
|
||||
func checkRiseEvent(state *HullBusterState, minNetChg, minROC float64) bool {
|
||||
return state.riseALBarsSince > 0 &&
|
||||
state.riseALNetChg > 0 &&
|
||||
state.riseALNetChg >= minNetChg &&
|
||||
(state.riseALNetChg/float64(state.riseALBarsSince)) >= minROC
|
||||
}
|
||||
|
||||
// 辅助函数:检查下降事件
|
||||
func checkFallEvent(state *HullBusterState, minNetChg, minROC float64) bool {
|
||||
return state.fallALBarsSince > 0 &&
|
||||
state.fallALNetChg > 0 &&
|
||||
state.fallALNetChg >= minNetChg &&
|
||||
(state.fallALNetChg/float64(state.fallALBarsSince)) >= minROC
|
||||
}
|
||||
|
||||
// 辅助函数:检查EMA上升事件
|
||||
func checkRiseEMEvent(state *HullBusterState, minNetChg, minROC float64) bool {
|
||||
return state.riseEMBarsSince > 0 &&
|
||||
state.riseEMNetChg > 0 &&
|
||||
state.riseEMNetChg >= minNetChg &&
|
||||
(state.riseEMNetChg/float64(state.riseEMBarsSince)) >= minROC
|
||||
}
|
||||
|
||||
// 辅助函数:检查EMA下降事件
|
||||
func checkFallEMEvent(state *HullBusterState, minNetChg, minROC float64) bool {
|
||||
return state.fallEMBarsSince > 0 &&
|
||||
state.fallEMNetChg > 0 &&
|
||||
state.fallEMNetChg >= minNetChg &&
|
||||
(state.fallEMNetChg/float64(state.fallEMBarsSince)) >= minROC
|
||||
}
|
||||
|
||||
// DEMA实现(双重指数移动平均)
|
||||
func DEMA(series *ta.Series, period int) *ta.Series {
|
||||
ema1 := ta.EMA(series, period)
|
||||
ema2 := ta.EMA(ema1, period)
|
||||
return ema1.Mul(2).Sub(ema2)
|
||||
}
|
||||
72
llmjob/7801.go
Normal file
72
llmjob/7801.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
)
|
||||
|
||||
// ConsecutiveState 存储每个品种的连续计数状态
|
||||
type ConsecutiveState struct {
|
||||
ups float64 // 连续上涨K线数
|
||||
dns float64 // 连续下跌K线数
|
||||
}
|
||||
|
||||
func J7801(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义超参数
|
||||
consecutiveBarsUp := pol.DefInt("consecutiveBarsUp", 3, core.PNorm(2, 12))
|
||||
consecutiveBarsDown := pol.DefInt("consecutiveBarsDown", 4, core.PNorm(2, 12))
|
||||
|
||||
return &strat.TradeStrat{
|
||||
Name: "Consecutive Up/Down Strategy",
|
||||
WarmupNum: 5, // 需要至少5根K线来确保前值有效
|
||||
HedgeOff: true, // 单向持仓模式
|
||||
EachMaxLong: 1, // 最大持仓1个多单
|
||||
EachMaxShort: 1, // 最大持仓1个空单
|
||||
TimeFrames: "8h",
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的状态
|
||||
s.More = &ConsecutiveState{
|
||||
ups: 0,
|
||||
dns: 0,
|
||||
}
|
||||
},
|
||||
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*ConsecutiveState)
|
||||
|
||||
// 获取当前和前一根K线的收盘价
|
||||
currentPrice := e.Close.Get(0)
|
||||
prevPrice := e.Close.Get(1)
|
||||
|
||||
// 更新连续上涨计数器
|
||||
if currentPrice > prevPrice {
|
||||
state.ups = state.ups + 1
|
||||
state.dns = 0 // 重置连续下跌计数
|
||||
} else if currentPrice < prevPrice {
|
||||
state.dns = state.dns + 1
|
||||
state.ups = 0 // 重置连续上涨计数
|
||||
} else {
|
||||
// 价格持平,重置两个计数器
|
||||
state.ups = 0
|
||||
state.dns = 0
|
||||
}
|
||||
|
||||
// 检查买入条件:连续上涨达到阈值且没有多单
|
||||
if state.ups >= float64(consecutiveBarsUp) && len(s.LongOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "ConsUpLE",
|
||||
})
|
||||
}
|
||||
|
||||
// 检查卖出条件:连续下跌达到阈值且没有空单
|
||||
if state.dns >= float64(consecutiveBarsDown) && len(s.ShortOrders) == 0 {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "ConsDnSE",
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
102
llmjob/7854.go
Normal file
102
llmjob/7854.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
// COG 自定义重心振荡器指标
|
||||
func COG(src *ta.Series, period int) *ta.Series {
|
||||
res := src.To("_cog", period)
|
||||
if res.Cached() {
|
||||
return res
|
||||
}
|
||||
|
||||
data := src.Range(0, period)
|
||||
if len(data) < period {
|
||||
return res.Append(math.NaN())
|
||||
}
|
||||
|
||||
var sum1, sum2 float64
|
||||
center := float64(period-1) / 2.0
|
||||
|
||||
for i := 0; i < period; i++ {
|
||||
weight := float64(i) - center
|
||||
sum1 += data[i] * weight
|
||||
sum2 += weight * weight
|
||||
}
|
||||
|
||||
if sum2 != 0 {
|
||||
cogVal := -sum1 / sum2
|
||||
return res.Append(cogVal)
|
||||
}
|
||||
return res.Append(0)
|
||||
}
|
||||
|
||||
func J7854(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
length := pol.DefInt("length", 9, core.PNorm(3, 50)) // COG计算周期
|
||||
length5 := pol.DefInt("length5", 3, core.PNorm(2, 20)) // RMA平滑周期
|
||||
windowsize := pol.DefInt("windowsize", 6, core.PNorm(2, 20)) // ALMA窗口周期
|
||||
offset := pol.Def("offset", 0.88, core.PNorm(0.5, 0.99)) // ALMA偏移量
|
||||
sigma := pol.Def("sigma", 7, core.PNorm(1, 10)) // ALMA高斯系数
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: length + 50, // 确保有足够的历史数据
|
||||
HedgeOff: true, // 单向持仓
|
||||
EachMaxLong: 1, // 最多1个多单
|
||||
EachMaxShort: 0, // 禁止做空
|
||||
TimeFrames: "8h",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 检查数据长度是否足够
|
||||
if e.Close.Len() < length {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算COG指标
|
||||
cog1 := COG(e.Close, length)
|
||||
|
||||
// 使用RMA平滑COG(对应原代码中的smooth选项,这里默认使用RMA)
|
||||
cogSmoothed := ta.RMA(cog1, length5)
|
||||
cog := cogSmoothed
|
||||
|
||||
// 计算ALMA触发线
|
||||
trigger := ta.ALMA(cog, windowsize, sigma, offset)
|
||||
|
||||
// 获取最新值
|
||||
cog1Val := cog1.Get(0)
|
||||
triggerVal := trigger.Get(0)
|
||||
|
||||
// 检查有效数据
|
||||
if math.IsNaN(cog1Val) || math.IsNaN(triggerVal) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检测COG1与触发线的交叉状态,只计算一次
|
||||
crossState := cog1.Cross(trigger)
|
||||
|
||||
// 检测COG1上穿触发线
|
||||
if crossState == 1 {
|
||||
// 产生买入信号
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "COG_Enter",
|
||||
})
|
||||
}
|
||||
|
||||
// 可选:添加退出逻辑
|
||||
// 这里可以根据需要添加退出条件,比如COG1下穿trigger线
|
||||
if crossState == -1 && len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "COG_Exit",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
61
llmjob/8179.go
Normal file
61
llmjob/8179.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
)
|
||||
|
||||
func J8179(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
lookback := pol.DefInt("lookback", 20, core.PNorm(5, 25)) // 随机振荡器计算周期
|
||||
kSmoothing := pol.DefInt("kSmoothing", 4, core.PNorm(1, 10)) // %K线平滑周期
|
||||
maLookback := pol.DefInt("maLookback", 19, core.PNorm(5, 30)) // %K线均线周期
|
||||
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: lookback + kSmoothing + maLookback + 10, // 确保有足够的历史数据
|
||||
EachMaxLong: 1, // 每个品种最多1个多单
|
||||
EachMaxShort: 1, // 每个品种最多1个空单
|
||||
TimeFrames: "8h",
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
|
||||
// 计算随机振荡器 KDJ
|
||||
// KDJ返回[K, D, RSV],其中K就是我们需要的%K线
|
||||
stochK, _, _ := ta.KDJ(e.High, e.Low, e.Close, lookback, kSmoothing, 3)
|
||||
|
||||
// 计算%K线的移动平均线
|
||||
maStochK := ta.SMA(stochK, maLookback)
|
||||
|
||||
// 检测交叉信号
|
||||
// Cross返回: 1=上穿, -1=下穿, 0=重合或未知
|
||||
crossSignal := stochK.Cross(maStochK)
|
||||
|
||||
// 根据交叉信号生成交易决策
|
||||
if crossSignal == 1 {
|
||||
// %K线上穿其均线,买入做多
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long_cross",
|
||||
Short: false,
|
||||
})
|
||||
// 同时平掉空单(如果有)
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
} else if crossSignal == -1 {
|
||||
// %K线下穿其均线,卖出做空
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short_cross",
|
||||
Short: true,
|
||||
})
|
||||
// 同时平掉多单(如果有)
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "close_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
226
llmjob/845.go
Normal file
226
llmjob/845.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banexg/utils"
|
||||
ta "github.com/banbox/banta"
|
||||
"math"
|
||||
)
|
||||
|
||||
/*
|
||||
需要多品种综合信息,品种数不得少于30
|
||||
做多:RSI超卖、RSI轻度超买跟随
|
||||
做空:全市场超卖动能枯竭做空
|
||||
*/
|
||||
|
||||
// State845 存储每个品种的RSI动态区间策略状态
|
||||
type State845 struct {
|
||||
position int // 当前持仓状态: 0=无持仓, 1=多头, -1=空头
|
||||
overBoughtDyn float64 // 动态超买水平
|
||||
overSoldDyn float64 // 动态超卖水平
|
||||
rsi float64
|
||||
maSub float64
|
||||
atr float64
|
||||
}
|
||||
|
||||
// RsiDynamic RSI动态区间反转策略
|
||||
func J845(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
rsiPeriod := pol.DefInt("rsiPeriod", 12, core.PNorm(7, 21))
|
||||
overBought := pol.Def("overBought", 76, core.PNorm(70, 90))
|
||||
overSold := pol.Def("overSold", 26, core.PNorm(20, 40))
|
||||
sensitivity := pol.Def("sensitivity", 2.5, core.PNorm(1.0, 5.0))
|
||||
|
||||
openRate, closeRate := float64(1), float64(0)
|
||||
prevRsiAvg, prevRsiBig, rsiDelta := float64(0), float64(0), float64(0)
|
||||
prevOverBuy, prevStamp, prevOpenRate := 0, int64(0), float64(0)
|
||||
stopLongUntil := int64(0)
|
||||
dayMS := int64(utils.TFToSecs("1d") * 1000)
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: 91, // 预热K线数量
|
||||
EachMaxLong: 1, // 最多开1个多单
|
||||
EachMaxShort: 1, // 最多开1个空单
|
||||
RunTimeFrames: []string{"1d"},
|
||||
BatchInOut: true,
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化每个品种的状态
|
||||
s.More = &State845{
|
||||
position: 0,
|
||||
overBoughtDyn: overBought,
|
||||
overSoldDyn: overSold,
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*State845)
|
||||
|
||||
rsi := ta.RSI(e.Close, rsiPeriod)
|
||||
rsiValue := rsi.Get(0)
|
||||
state.rsi = rsiValue
|
||||
state.atr = ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
|
||||
ma20 := ta.SMA(e.Close, 20).Get(0)
|
||||
ma90 := ta.SMA(e.Close, 90).Get(0)
|
||||
state.maSub = ma20 - ma90
|
||||
|
||||
// 计算动态超买超卖水平
|
||||
calculateDynamicLevels(rsi, sensitivity, overBought, overSold, state)
|
||||
|
||||
// 生成交易信号
|
||||
if state.position == 0 && openRate > 0 && e.TimeStop >= stopLongUntil { // 无持仓
|
||||
if rsiValue < state.overSoldDyn {
|
||||
// RSI跌破超卖水平,建立多头仓位
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
CostRate: openRate,
|
||||
Log: true,
|
||||
})
|
||||
state.position = 1
|
||||
}
|
||||
} else if state.position == -1 { // 持有空头仓位
|
||||
if rsiValue <= state.overBoughtDyn {
|
||||
// RSI从超买区域回落,平掉多头仓位
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit2",
|
||||
Dirt: core.OdDirtLong,
|
||||
EnterTag: "long2",
|
||||
Log: true,
|
||||
})
|
||||
state.position = 0
|
||||
}
|
||||
} else if state.position == 1 { // 持有多头仓位
|
||||
if rsiValue >= state.overSoldDyn {
|
||||
// RSI从超卖区域回升,平掉多头仓位
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit1",
|
||||
Dirt: core.OdDirtLong,
|
||||
EnterTag: "long",
|
||||
Log: true,
|
||||
})
|
||||
state.position = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
rsiAvg, rsiBig := float64(0), float64(0)
|
||||
overBuyNum, curStamp := 0, int64(0)
|
||||
var st *strat.TradeStrat
|
||||
for _, s := range jobs {
|
||||
state, _ := s.More.(*State845)
|
||||
rsiAvg += state.rsi
|
||||
if state.rsi >= 68 {
|
||||
rsiBig += 1
|
||||
}
|
||||
if state.rsi > state.overBoughtDyn {
|
||||
overBuyNum += 1
|
||||
}
|
||||
curStamp = s.Env.TimeStop
|
||||
st = s.Strat
|
||||
}
|
||||
numFlt := float64(len(jobs))
|
||||
rsiAvg /= numFlt
|
||||
rsiBig /= numFlt
|
||||
if prevRsiAvg >= 70 && rsiAvg < prevRsiAvg && prevRsiBig > 0.5 {
|
||||
// 极端过热转向,平仓,禁止做多2周
|
||||
openRate, closeRate = -2, 1
|
||||
if prevRsiBig > 0.65 && prevOpenRate < 0 {
|
||||
openRate = -3
|
||||
stopLongUntil = curStamp + dayMS*14
|
||||
}
|
||||
} else if rsiAvg > 77 && rsiBig > 0.75 || rsiAvg > 70 && rsiBig > 0.5 && (rsiAvg-prevRsiAvg)*5 < rsiDelta {
|
||||
// 严重过热,平仓一半,禁止开仓
|
||||
openRate, closeRate = -1, 0.5
|
||||
if curStamp < stopLongUntil {
|
||||
stopLongUntil = max(stopLongUntil, curStamp+dayMS*10)
|
||||
}
|
||||
} else if rsiAvg > 70 && rsiBig > 0.5 {
|
||||
// 中度过热
|
||||
openRate, closeRate = 0.6, 0
|
||||
if curStamp < stopLongUntil {
|
||||
stopLongUntil = max(stopLongUntil, curStamp+dayMS*6)
|
||||
}
|
||||
} else if rsiAvg > 60 && rsiBig > 0.3 {
|
||||
// 轻度过热
|
||||
openRate, closeRate = 0.8, 0
|
||||
} else {
|
||||
openRate, closeRate = 1, 0
|
||||
}
|
||||
if closeRate > 0 {
|
||||
for _, s := range jobs {
|
||||
if s.OrderNum > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "cut",
|
||||
Dirt: core.OdDirtLong,
|
||||
ExitRate: closeRate,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if overBuyNum >= 2 || overBuyNum+prevOverBuy >= 3 {
|
||||
// 超买跟随,仅适用于多数品种一同上涨,所以要限制数量
|
||||
for _, s := range jobs {
|
||||
state, _ := s.More.(*State845)
|
||||
if state.rsi > state.overBoughtDyn {
|
||||
// RSI突破超买水平,追涨买入
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long2",
|
||||
CostRate: openRate,
|
||||
Log: true,
|
||||
})
|
||||
state.position = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
// 超买做空机制,仅对特定品种表现良好,对于动态品种表现更差
|
||||
//if openRate <= -3 {
|
||||
// for _, s := range jobs {
|
||||
// state, _ := s.More.(*State845)
|
||||
// if state.maSub < 0 || state.rsi < 76 || len(s.ShortOrders) > 0 {
|
||||
// continue
|
||||
// }
|
||||
// // 牛市中全市场超买时做空;不可在ObBatchJobs中开单,会变差
|
||||
// s.OpenOrder(&strat.EnterReq{
|
||||
// Tag: "short1",
|
||||
// //Short: true,
|
||||
// TakeProfitVal: state.atr * 2,
|
||||
// StopLossVal: state.atr * 2,
|
||||
// Log: true,
|
||||
// })
|
||||
// }
|
||||
//}
|
||||
if openRate < 1 {
|
||||
st.WriteOutput(fmt.Sprintf("num: %d, rate: %.3f, rsi: %.3f, rsiBig: %.3f",
|
||||
len(jobs), openRate, rsiAvg, rsiBig), true)
|
||||
}
|
||||
if curStamp > prevStamp {
|
||||
rsiDelta = rsiAvg - prevRsiAvg
|
||||
prevRsiAvg = rsiAvg
|
||||
prevRsiBig = rsiBig
|
||||
prevStamp = curStamp
|
||||
prevOpenRate = openRate
|
||||
prevOverBuy = overBuyNum
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// calculateDynamicLevels 计算动态超买超卖水平
|
||||
func calculateDynamicLevels(rsi *ta.Series, sensitivity, overBought, overSold float64, state *State845) {
|
||||
// 计算近期RSI波动率(标准差)
|
||||
stdDev, _ := ta.StdDevBy(rsi, 14, 1)
|
||||
rsiVolatility := stdDev.Get(0)
|
||||
|
||||
// 计算动态调整因子
|
||||
adjustmentFactor := sensitivity * rsiVolatility / 10
|
||||
|
||||
// 计算动态超买超卖水平
|
||||
state.overBoughtDyn = overBought + adjustmentFactor*(100-overBought)/100
|
||||
state.overSoldDyn = overSold - adjustmentFactor*overSold/100
|
||||
|
||||
// 确保动态水平在合理范围内
|
||||
state.overBoughtDyn = math.Max(70, math.Min(95, state.overBoughtDyn))
|
||||
state.overSoldDyn = math.Max(5, math.Min(40, state.overSoldDyn))
|
||||
}
|
||||
66
llmjob/README.md
Normal file
66
llmjob/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
## 不可用的
|
||||
3746: 仅适用于BTC,且订单太少
|
||||
1408: bug导致的完美曲线:触发价入场时止盈止损可能错误
|
||||
1662: 因止盈止损触发价未检查是否合理的bug导致虚假净值曲线
|
||||
5689: 因止损未检查bug导致表现看起来很好,不可用
|
||||
5609: 仅对ETH有效
|
||||
3746: 仅对BTC生效,订单不多
|
||||
5115: 波动太大,不可用
|
||||
2439: 已失效,波动太大
|
||||
3127: 不稳定,且仅针对BTC
|
||||
5129: 15m,不可用,且过于复杂
|
||||
4316: 15m,不可用,且过于复杂
|
||||
3121: 5m 净亏
|
||||
199: 4h 波动较大
|
||||
4670: 仅适用BTC,波动大
|
||||
3614: 仅适用BTC,1h波动大
|
||||
3707: 仅适用BTC
|
||||
656: 近期回撤较大
|
||||
|
||||
## 不够好
|
||||
4854: 3d波动略大,不止损
|
||||
4267: 一般
|
||||
3644: 不够好,波动大
|
||||
3827: 不够好
|
||||
2391: 波动大
|
||||
3583: 15m,不够稳定,闲置较多
|
||||
4067: 4h,较复杂,不太稳定
|
||||
4994: 4h,上升趋势明显,但局部波动较大
|
||||
5743: 8h,趋势,最近回撤明显
|
||||
559: 8h,经典MACD金叉死叉
|
||||
128: 4h,较复杂,最近回撤明显
|
||||
2251: 4h,近期回撤明显
|
||||
592: 8h, 最近略有回撤
|
||||
2874: 4h,最近略有回撤
|
||||
3137: 15m,long之前曾有回撤
|
||||
3623: 网格马丁,和已有重合
|
||||
4627: 15m, 2个信号不错,但大部分时间无信号
|
||||
3955: 8h,趋势策略,局部波动略大
|
||||
4009: 1d,Sell不错,但大部分时间信号少
|
||||
1357: 8h, 趋势策略,近期横盘
|
||||
3956: 8h, 趋势策略,近期横盘
|
||||
3683: 8h, 趋势策略,近期横盘
|
||||
5763: 8h, 趋势策略,局部波动略大
|
||||
3464: 4h, 趋势策略,局部波动稍微大点
|
||||
4526: 8h,马丁&布林带,和已有重合
|
||||
4513: 8h, 趋势策略,局部波动略大
|
||||
2775: 8h, 最近横盘
|
||||
1665: 8h, 较复杂,波动稍大
|
||||
2876: 15m波动略大,资金闲置多,不够好;251010亏不少
|
||||
3087: 分批加仓,固定止盈;适合波动大的小币种,不如1525稳定,原理类似,暂不使用
|
||||
2625: 待定,1h,波动略大;好像有bug
|
||||
|
||||
2984: 还可以,和3992比较像
|
||||
4583: 还可以,近期回撤
|
||||
3992: 还可以
|
||||
4225: 还可以
|
||||
1569: 1h还可以,近期有回撤
|
||||
4034: 8h还可以,近期不佳
|
||||
2523: 3d,胜率高,可能过拟合,失效回撤大
|
||||
|
||||
## 可用的
|
||||
3782: 15m网格,很好
|
||||
3762: 15m很好,超跌反弹
|
||||
845: 1d RSI超卖超买动能跟随,不错
|
||||
1525: 5m很稳定,但瞬时压力大,加仓太多,使用不当可能爆仓
|
||||
214
llmjob/his/3762.go
Normal file
214
llmjob/his/3762.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package his
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/com"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm/ormo"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/log"
|
||||
"github.com/banbox/banexg/utils"
|
||||
ta "github.com/banbox/banta"
|
||||
"go.uber.org/zap"
|
||||
"math"
|
||||
)
|
||||
|
||||
/*
|
||||
超跌反弹策略。30个品种为宜
|
||||
做空部分没有利润,禁用
|
||||
*/
|
||||
|
||||
type State3762 struct {
|
||||
NoLongUntil int64
|
||||
NoShortUntil int64
|
||||
}
|
||||
|
||||
func J3762(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义策略参数
|
||||
p := pol.DefInt("p", 20, core.PNorm(5, 50))
|
||||
d := pol.Def("d", 30.0, core.PNorm(5, 50))
|
||||
mult := pol.Def("mult", 1.5, core.PNorm(1.0, 3.0))
|
||||
slRate := pol.Def("slRate", 25, core.PNorm(0, 50))
|
||||
needlong := pol.Def("needlong", 1.0, core.PUniform(0, 1)) > 0.5
|
||||
needshort := pol.Def("needshort", 0.0, core.PUniform(0, 1)) > 0.5
|
||||
|
||||
dayMS := int64(utils.TFToSecs("1d") * 1000)
|
||||
|
||||
lenAtr := 20
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: baseAtrLen * 2, // 预热K线数量,确保有足够数据计算指标
|
||||
RunTimeFrames: []string{"15m"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &State3762{}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
m, _ := s.More.(*State3762)
|
||||
// 计算基础布林带指标
|
||||
sma := ta.SMA(e.Close, p)
|
||||
ma7 := ta.SMA(e.Close, 7).Get(0)
|
||||
ma30 := ta.SMA(e.Close, 30).Get(0)
|
||||
multCur := mult
|
||||
if ma7 < ma30 {
|
||||
// 熊市下移下限
|
||||
multCur *= 1.35
|
||||
}
|
||||
stddev := ta.StdDev(e.Close, p).Get(0) * multCur
|
||||
upperBB := sma.Get(0) + stddev
|
||||
lowerBB := sma.Get(0) - stddev
|
||||
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
|
||||
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
|
||||
|
||||
// 计算百分比偏离轨道
|
||||
atrRt := d
|
||||
if ma7 < ma30 {
|
||||
// 熊市下移下限
|
||||
atrRt *= 1.35
|
||||
}
|
||||
upex2 := sma.Get(0) + atrBase*atrRt
|
||||
dnex2 := sma.Get(0) - atrBase*atrRt
|
||||
|
||||
// 计算最终轨道
|
||||
upexPrice := max(upperBB, (upex2+upperBB)/2)
|
||||
dnexPrice := min(lowerBB, (dnex2+lowerBB)/2)
|
||||
|
||||
// 获取当前价格
|
||||
closePrice := e.Close.Get(0)
|
||||
openPrice := e.Open.Get(0)
|
||||
|
||||
// 生成交易信号
|
||||
exitSignal := closePrice > openPrice // 绿色K线信号
|
||||
|
||||
if math.IsNaN(upexPrice) || math.IsNaN(dnexPrice) {
|
||||
return
|
||||
}
|
||||
slRange := min(atrBase*slRate, closePrice*0.25)
|
||||
|
||||
// 执行交易逻辑
|
||||
if !s.IsWarmUp {
|
||||
s.Strat.WriteOutput(fmt.Sprintf("%s upp: %.6f, low: %.6f upex2: %.6f, dnex2: %.6f upex: %.6f, dnex: %.6f slRange: %.6f",
|
||||
e.Symbol, upperBB, lowerBB, upex2, dnex2, upexPrice, dnexPrice, slRange), true)
|
||||
}
|
||||
if needlong {
|
||||
if len(s.LongOrders) == 0 {
|
||||
// 使用限价单在下轨价格做多,不依赖当前价格是否突破轨道
|
||||
if e.TimeStop >= m.NoLongUntil {
|
||||
curPrice := com.GetPrice(e.Symbol, banexg.OdSideBuy)
|
||||
if math.Abs(curPrice/closePrice-1) > 0.01 {
|
||||
log.Info("bad price from GetPrice", zap.String("pair", e.Symbol),
|
||||
zap.Float64("val", curPrice), zap.Float64("close", closePrice))
|
||||
}
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Long",
|
||||
Limit: dnexPrice, // 以下轨价格作为限价单
|
||||
StopLossVal: slRange,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for _, od := range s.LongOrders {
|
||||
if od.Status == ormo.InOutStatusInit {
|
||||
err := od.SetEnterLimit(dnexPrice)
|
||||
if err != nil {
|
||||
log.Error("SetEnterLimit fail", zap.Error(err))
|
||||
} else {
|
||||
err = od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: dnexPrice - slRange,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("SetStopLoss fail", zap.Error(err))
|
||||
}
|
||||
}
|
||||
} else if od.Status == ormo.InOutStatusFullEnter {
|
||||
barNum := e.BarCount(od.Enter.UpdateAt)
|
||||
if barNum > 2 && od.ProfitRate < 0 {
|
||||
// 持仓2个k线,出现亏损,平仓
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Exit bad Long",
|
||||
Dirt: core.OdDirtLong,
|
||||
Log: true,
|
||||
})
|
||||
m.NoLongUntil = e.TimeStop + dayMS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if needshort {
|
||||
if len(s.ShortOrders) == 0 {
|
||||
// 使用限价单在上轨价格做空,不依赖当前价格是否突破轨道
|
||||
if e.TimeStop >= m.NoShortUntil {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "Short",
|
||||
Short: true,
|
||||
Limit: upexPrice, // 以上轨价格作为限价单
|
||||
StopLossVal: slRange,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
for _, od := range s.ShortOrders {
|
||||
if od.Status == ormo.InOutStatusInit {
|
||||
err := od.SetEnterLimit(upexPrice)
|
||||
if err != nil {
|
||||
log.Error("SetEnterLimit fail", zap.Error(err))
|
||||
} else {
|
||||
err = od.SetStopLoss(&ormo.ExitTrigger{
|
||||
Price: upexPrice + slRange,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("SetStopLoss fail", zap.Error(err))
|
||||
}
|
||||
}
|
||||
} else if od.Status == ormo.InOutStatusFullEnter {
|
||||
barNum := e.BarCount(od.Enter.UpdateAt)
|
||||
if barNum > 2 && od.ProfitRate < 0 {
|
||||
// 持仓2个k线,出现亏损,平仓
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Exit bad short",
|
||||
Dirt: core.OdDirtShort,
|
||||
Log: true,
|
||||
})
|
||||
m.NoShortUntil = e.TimeStop + dayMS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 平仓条件:绿色K线
|
||||
if exitSignal {
|
||||
if len(s.LongOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Exit Long",
|
||||
Dirt: core.OdDirtLong,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if len(s.ShortOrders) > 0 {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "Exit Short",
|
||||
Dirt: core.OdDirtShort,
|
||||
Log: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) {
|
||||
if chgType == core.OdChgExitFill && od.ExitTag == core.ExitTagStopLoss {
|
||||
m, _ := s.More.(*State3762)
|
||||
if od.Short {
|
||||
m.NoShortUntil = od.ExitAt + dayMS
|
||||
} else {
|
||||
m.NoLongUntil = od.ExitAt + dayMS
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
270
llmjob/his/3782.go
Normal file
270
llmjob/his/3782.go
Normal file
@@ -0,0 +1,270 @@
|
||||
package his
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
ta "github.com/banbox/banta"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
"math"
|
||||
)
|
||||
|
||||
/*
|
||||
需要多品种综合市场信息,品种数不可少于30
|
||||
*/
|
||||
|
||||
// ZoneTradeState 用于存储每个交易区间的状态信息
|
||||
type ZoneTradeState struct {
|
||||
LastTradeTime2 int64 // 买入区间2的最后交易时间
|
||||
LastTradeTime3 int64 // 买入区间3的最后交易时间
|
||||
LastTradeTime4 int64 // 买入区间4的最后交易时间
|
||||
LastSellTime int64 // 最后卖出时间
|
||||
atrBase float64
|
||||
score float64
|
||||
upFlag float64
|
||||
}
|
||||
|
||||
func J3782(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
// 定义可调超参数
|
||||
maPeriod := pol.DefInt("maPeriod", 100, core.PNorm(20, 200))
|
||||
baseRetracement := pol.Def("baseRetracement", 0.08, core.PNorm(0.05, 0.2))
|
||||
retracementRate := pol.Def("retracementRate", 1.8, core.PNorm(1.5, 2.5))
|
||||
profitRate := pol.Def("profitRate", 0.03, core.PNorm(0.015, 0.06))
|
||||
tradeDelay := pol.Def("tradeDelay", 40, core.PNorm(10, 100))
|
||||
slRate := pol.Def("slRate", 63, core.PNorm(5, 35))
|
||||
|
||||
// 计算各回撤比例
|
||||
retracement2 := baseRetracement
|
||||
retracement3 := baseRetracement * retracementRate
|
||||
retracement4 := baseRetracement * retracementRate * 1.33
|
||||
|
||||
lenAtr := 20
|
||||
baseAtrLen := int(math.Round(float64(lenAtr) * 4.3))
|
||||
rates := make([]float64, 0, 10)
|
||||
|
||||
// 最大交易数量
|
||||
maxTrades2 := 2
|
||||
maxTrades3 := 3
|
||||
maxTrades4 := 4
|
||||
|
||||
openPairs := 0.0
|
||||
var pairLock deadlock.Mutex
|
||||
addThres, firstRate := 1.0, 1.0
|
||||
avgAtrBase := 2.5
|
||||
maxPair := 3.0
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: maPeriod,
|
||||
BatchInfo: true,
|
||||
BatchInOut: true,
|
||||
RunTimeFrames: []string{"15m"},
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
// 初始化状态信息
|
||||
s.More = &ZoneTradeState{}
|
||||
},
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
return []*strat.PairSub{
|
||||
{"_cur_", "4h", baseAtrLen * 2},
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
e := s.Env
|
||||
state, _ := s.More.(*ZoneTradeState)
|
||||
|
||||
// 计算MA100
|
||||
ma100 := ta.SMA(e.Close, maPeriod)
|
||||
ma100Val := ma100.Get(0)
|
||||
|
||||
// 计算交易价格水平
|
||||
curRate := max(addThres, 1+state.score/10)
|
||||
itemRate := max(0.7, min(math.Sqrt(state.atrBase/avgAtrBase), 1.4))
|
||||
buyPrice2 := ma100Val * (1 - retracement2*curRate*itemRate*firstRate)
|
||||
buyPrice3 := ma100Val * (1 - retracement3*curRate*itemRate)
|
||||
buyPrice4 := ma100Val * (1 - retracement4*curRate*itemRate)
|
||||
sellPrice := ma100Val * (1 + profitRate)
|
||||
|
||||
// 获取当前价格
|
||||
lowPrice := e.Low.Get(0)
|
||||
highPrice := e.High.Get(0)
|
||||
|
||||
// 检查交易间隔条件
|
||||
currentBarTime := e.TimeStop
|
||||
|
||||
lastEnt := max(state.LastTradeTime2, state.LastTradeTime3, state.LastTradeTime4)
|
||||
canTrade := e.BarCount(lastEnt) >= 10
|
||||
canTrade2 := canTrade && e.BarCount(state.LastTradeTime2) >= tradeDelay
|
||||
canTrade3 := canTrade && e.BarCount(state.LastTradeTime3) >= tradeDelay
|
||||
canTrade4 := canTrade && e.BarCount(state.LastTradeTime4) >= tradeDelay
|
||||
canSell := e.BarCount(state.LastSellTime) >= tradeDelay
|
||||
|
||||
// 获取当前总开仓数量,与原始策略的strategy.opentrades保持一致
|
||||
openTrades := len(s.LongOrders) + len(s.ShortOrders)
|
||||
|
||||
if lowPrice <= buyPrice4 && openTrades < maxTrades4 && canTrade4 {
|
||||
pairLock.Lock()
|
||||
allow := false
|
||||
if openPairs < maxPair {
|
||||
openPairs += 3
|
||||
allow = true
|
||||
}
|
||||
pairLock.Unlock()
|
||||
if allow {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "BuyZone4",
|
||||
CostRate: 3,
|
||||
Log: true,
|
||||
})
|
||||
state.LastTradeTime4 = currentBarTime
|
||||
}
|
||||
}
|
||||
if lowPrice <= buyPrice3 && openTrades < maxTrades3 && canTrade3 {
|
||||
pairLock.Lock()
|
||||
allow := false
|
||||
if openPairs < maxPair {
|
||||
openPairs += 1.5
|
||||
allow = true
|
||||
}
|
||||
pairLock.Unlock()
|
||||
if allow {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "BuyZone3",
|
||||
CostRate: 1.5,
|
||||
Log: true,
|
||||
})
|
||||
state.LastTradeTime3 = currentBarTime
|
||||
}
|
||||
}
|
||||
if lowPrice <= buyPrice2 && openTrades < maxTrades2 && canTrade2 {
|
||||
pairLock.Lock()
|
||||
allow := false
|
||||
if openPairs < maxPair {
|
||||
openPairs += 1
|
||||
allow = true
|
||||
}
|
||||
pairLock.Unlock()
|
||||
if allow {
|
||||
s.OpenOrder(&strat.EnterReq{
|
||||
Tag: "BuyZone2",
|
||||
CostRate: 1,
|
||||
Log: true,
|
||||
})
|
||||
state.LastTradeTime2 = currentBarTime
|
||||
}
|
||||
}
|
||||
|
||||
// 检查卖出条件
|
||||
if s.OrderNum > 0 {
|
||||
curPrice := e.Close.Get(0)
|
||||
slRange := slRate * itemRate * curPrice / 100
|
||||
avgPrice := s.PositionAvgPrice(core.OdDirtLong, "")
|
||||
if curPrice+slRange <= avgPrice {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "StopLoss",
|
||||
Log: true,
|
||||
})
|
||||
state.LastSellTime = currentBarTime
|
||||
} else {
|
||||
exitPrice := avgPrice*(1+profitRate)*0.4 + sellPrice*0.6
|
||||
if openTrades > 0 && highPrice >= exitPrice && canSell {
|
||||
s.CloseOrders(&strat.ExitReq{
|
||||
Tag: "SellAll",
|
||||
Log: true,
|
||||
})
|
||||
state.LastSellTime = currentBarTime
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
|
||||
score := float64(0)
|
||||
if ta.RSI(e.Close, 14).Get(0) < 30 {
|
||||
score += 1
|
||||
}
|
||||
roc := ta.ROC(e.Close, 20).Get(0)
|
||||
if roc < -20 {
|
||||
score += 2
|
||||
} else if roc < -10 {
|
||||
score += 1
|
||||
}
|
||||
plusDi, minusDi := ta.PluMinDI(e.High, e.Low, e.Close, 14)
|
||||
adx := ta.ADX(e.High, e.Low, e.Close, 14).Get(0)
|
||||
if minusDi.Get(0) > plusDi.Get(0) {
|
||||
if adx > 40 {
|
||||
score += 2
|
||||
} else if adx > 25 {
|
||||
score += 1
|
||||
}
|
||||
}
|
||||
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
|
||||
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
|
||||
rates = append(rates, atrBase/(e.Close.Get(0)*0.01))
|
||||
//openRate := 0.006 * closePrice / atrBase
|
||||
//if openRate < 1 {
|
||||
// openRate = math.Sqrt(openRate)
|
||||
//}
|
||||
//openRate = max(0.7, min(1.5, openRate))
|
||||
ma7 := ta.SMA(e.Close, 7).Get(0)
|
||||
ma20 := ta.SMA(e.Close, 20)
|
||||
ma20V := ma20.Get(0)
|
||||
ma99 := ta.SMA(e.Close, 99).Get(0)
|
||||
if ma99 > ma20V {
|
||||
score += 1
|
||||
}
|
||||
state, _ := s.More.(*ZoneTradeState)
|
||||
state.score = score
|
||||
state.upFlag = 0
|
||||
state.atrBase = atrBase / (e.Close.Get(0) * 0.01)
|
||||
if ma20V > ma99 && ma20V > ma20.Get(2) {
|
||||
state.upFlag = 1
|
||||
} else if ma7 < min(ma20V, ma99) {
|
||||
state.upFlag = -1
|
||||
}
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
pairLock.Lock()
|
||||
openPairs = 0
|
||||
pairLock.Unlock()
|
||||
},
|
||||
OnBatchInfos: func(tf string, jobs map[string]*strat.JobEnv) {
|
||||
if len(jobs) < 3 {
|
||||
return
|
||||
}
|
||||
downNum, downHeavyNum := float64(0), float64(0)
|
||||
atrRates := make([]float64, 0, len(jobs))
|
||||
for _, j := range jobs {
|
||||
state, _ := j.Job.More.(*ZoneTradeState)
|
||||
if state.score >= 5 {
|
||||
downHeavyNum += 1
|
||||
}
|
||||
if state.score >= 3 {
|
||||
downNum += 1
|
||||
}
|
||||
atrRates = append(atrRates, state.atrBase)
|
||||
}
|
||||
jobNum := float64(len(jobs))
|
||||
downRate := downNum / jobNum
|
||||
downHeavyRate := downHeavyNum / jobNum
|
||||
avgAtrBase = atrRates[len(jobs)/2]
|
||||
if downHeavyRate >= 0.8 {
|
||||
// 极端下跌
|
||||
addThres, firstRate = 1.6, 1.2
|
||||
maxPair = jobNum * 0.5
|
||||
} else if downRate >= 0.85 && downHeavyRate >= 0.6 {
|
||||
// 严重下跌
|
||||
addThres, firstRate = 1.45, 1.15
|
||||
maxPair = jobNum * 0.55
|
||||
} else if downRate >= 0.7 && downRate <= 0.85 {
|
||||
// 市场中度下跌
|
||||
addThres, firstRate = 1.3, 1.1
|
||||
maxPair = jobNum * 0.6
|
||||
} else if downRate >= 0.5 && downRate <= 0.7 {
|
||||
// 轻度下跌
|
||||
addThres, firstRate = 1.15, 1.05
|
||||
maxPair = jobNum * 0.7
|
||||
} else {
|
||||
// 轻度或未下跌
|
||||
maxPair = jobNum
|
||||
addThres, firstRate = 1, 1
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
66
llmjob/his/3782.yml
Normal file
66
llmjob/his/3782.yml
Normal file
@@ -0,0 +1,66 @@
|
||||
env: dry_run
|
||||
market_type: linear
|
||||
leverage: 10
|
||||
time_start: "20250101"
|
||||
time_end: "20251112"
|
||||
stake_amount: 30
|
||||
concur_num: 3
|
||||
# stake_pct: 30
|
||||
max_stake_amt: 5000
|
||||
pairs: []
|
||||
relay_sim_unfinish: false
|
||||
stake_currency: [USDT]
|
||||
wallet_amounts:
|
||||
USDT: 1400
|
||||
run_policy:
|
||||
- name: llmjob:H3762
|
||||
stake_rate: 5
|
||||
refine_tf: 1m
|
||||
- name: llmjob:845
|
||||
stake_rate: 5
|
||||
refine_tf: 1m
|
||||
- name: llmjob:H3782
|
||||
stake_rate: 1
|
||||
refine_tf: 1m
|
||||
# - name: llmjob:1525
|
||||
# run_timeframes: [5m]
|
||||
# stake_rate: 0.4
|
||||
# params:
|
||||
# max_open_pair: 5
|
||||
# max_pair: 30
|
||||
# - name: llmjob:2523
|
||||
# stake_rate: 20
|
||||
pairmgr:
|
||||
cron: '0 0 0 1 */2 *' # 秒 分钟 小时 日 月 星期
|
||||
offset: 0
|
||||
limit: 999
|
||||
force_filters: false
|
||||
pos_on_rotation: close
|
||||
pairlists:
|
||||
- name: VolumePairList
|
||||
limit_rate: 0.7
|
||||
limit: 360
|
||||
min_value: 100000
|
||||
cache_secs: 7200
|
||||
back_period: 30d
|
||||
- name: PriceFilter # 价格过滤器
|
||||
max_unit_value: 200 # 最大允许的单位价格变动对应的价值(针对定价货币,一般是USDT)。
|
||||
precision: 0.0015 # 按价格精度过滤交易对,默认要求价格变动最小单位是0.1%
|
||||
min: 0.0001 # 最低价格
|
||||
max: 500000 # 最高价格
|
||||
- name: RateOfChangeFilter # 波动性过滤器
|
||||
back_days: 10 # 回顾的K线天数
|
||||
min: 0.03 # 最小价格变动比率
|
||||
max: 3 # 最大价格变动比率
|
||||
cache_secs: 1440 # 缓存时间,秒
|
||||
- name: SpreadFilter # 流动性过滤器
|
||||
max_ratio: 0.005 # 公式:1-bid/ask,买卖价差占价格的最大比率
|
||||
- name: AgeFilter # 按标的的上市天数过滤
|
||||
min: 180
|
||||
- name: VolatilityFilter # 波动性过滤器,公式:std(log(c/c1)) * sqrt(back_days)
|
||||
back_days: 10 # 回顾的K线天数
|
||||
max: 1 # 波动分数最大值,此值越大,允许一些在1d级别上变化非常剧烈的标的
|
||||
min: 0.03 # 波动分数最小值,此值越小,允许一些在1d级别上变化非常小的标的
|
||||
- name: OffsetFilter
|
||||
rate: 0.5
|
||||
limit: 30
|
||||
81
llmjob/main.go
Normal file
81
llmjob/main.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package llmjob
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banstagy/llmjob/his"
|
||||
)
|
||||
|
||||
func init() {
|
||||
strat.AddStratGroup("llmjob", map[string]strat.FuncMakeStrat{
|
||||
"H3762": his.J3762,
|
||||
"H3782": his.J3782,
|
||||
"32": J32,
|
||||
"419": J419,
|
||||
"570": J570,
|
||||
"1665": J1665,
|
||||
"2876": J2876,
|
||||
"3087": J3087,
|
||||
"2625": J2625,
|
||||
"2984": J2984,
|
||||
"3782": J3782,
|
||||
"3762": J3762,
|
||||
"4583": J4583,
|
||||
"5609": J5609,
|
||||
"3992": J3992,
|
||||
"2523": J2523,
|
||||
"4225": J4225,
|
||||
"4267": J4267,
|
||||
"3644": J3644,
|
||||
"3746": J3746,
|
||||
"5689": J5689,
|
||||
"5115": J5115,
|
||||
"4067": J4067,
|
||||
"1525": J1525Sim,
|
||||
"4994": J4994,
|
||||
"5743": J5743,
|
||||
"845": J845,
|
||||
"3127": J3127,
|
||||
"1662": J1662,
|
||||
"4034": J4034,
|
||||
"5129": J5129,
|
||||
"559": J559,
|
||||
"128": J128,
|
||||
"2251": J2251,
|
||||
"4316": J4316,
|
||||
"3121": J3121,
|
||||
"199": J199,
|
||||
"4670": J4670,
|
||||
"3614": J3614,
|
||||
"592": J592,
|
||||
"3707": J3707,
|
||||
"2874": J2874,
|
||||
"1569": J1569,
|
||||
"3137": J3137,
|
||||
"2391": J2391,
|
||||
"3955": J3955,
|
||||
"1357": J1357,
|
||||
"3956": J3956,
|
||||
"3939": J3939,
|
||||
"656": J656,
|
||||
"3683": J3683,
|
||||
"4217": J4217,
|
||||
"5763": J5763,
|
||||
"3464": J3464,
|
||||
"4513": J4513,
|
||||
"2775": J2775,
|
||||
"4009": J4009,
|
||||
"6697": J6697,
|
||||
"6969": J6969,
|
||||
"7699": J7699,
|
||||
"7096": J7096,
|
||||
"7050": J7050,
|
||||
"3827": J3827,
|
||||
"7593": J7593,
|
||||
"8179": J8179,
|
||||
"7801": J7801,
|
||||
"7046": J7046,
|
||||
"7854": J7854,
|
||||
|
||||
"587": J587,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user