init
This commit is contained in:
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user