This commit is contained in:
2026-08-24 20:53:21 +08:00
commit 6e78bba16a
287 changed files with 32443 additions and 0 deletions

214
llmjob/his/3762.go Normal file
View 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
View 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
View 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