Files
banstagy/old/eat.go
2026-08-24 20:53:21 +08:00

166 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package old
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/strat"
"github.com/banbox/banexg/log"
ta "github.com/banbox/banta"
"go.uber.org/zap"
"math"
"sort"
)
var (
lenAtr = 9
rangeRate = 0.6 // 预计bar利润区间比率:60%
ShakeTop = 0.2 // 只对分数前20%的标的进行震荡交易
minProfitPct = 0.01 // 最小目标盈利百分比
)
type TMore struct {
symbol string
shakeScore float64 // 震荡分数:(1-平均实体比率)*平均bar波动比率
btmPrice float64 // 平均向下突破比率
topPrice float64 // 平均向上突破比率
shakeOK bool // 是否允许震荡中开单
shakeHit float64 // 震荡首先触发位置:-1触发顶部,1触发底部
}
func Eat(pol *config.RunPolicyConfig) *strat.TradeStrat {
baseAtrLen := int(math.Round(float64(lenAtr) * 5))
return &strat.TradeStrat{
Version: 1,
WarmupNum: max(30, baseAtrLen),
MinTfScore: 0.75,
BatchInfo: true,
OnStartUp: func(s *strat.StratJob) {
s.More = &TMore{
symbol: s.Symbol.Symbol,
}
},
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
return []*strat.PairSub{
{Pair: "_cur_", TimeFrame: "8h", WarmupNum: 30},
}
},
OnBar: func(s *strat.StratJob) {
e := s.Env
h, l, c := e.High.Get(0), e.Low.Get(0), e.Close.Get(0)
m, _ := s.More.(*TMore)
if m.shakeOK {
// 允许震荡开单
if m.shakeHit == 0 {
if h >= m.topPrice {
m.shakeHit = -1
} else if l <= m.btmPrice {
m.shakeHit = 1
}
}
if s.OrderNum == 0 && m.shakeHit != 0 {
slRange := (m.topPrice - m.btmPrice)
tag := "long"
trigPrice := m.btmPrice
takeProfit := m.topPrice
if m.shakeHit < 0 {
tag = "short"
trigPrice = m.topPrice
takeProfit = m.btmPrice
}
stopLoss := trigPrice - slRange*m.shakeHit
slDst := (c - stopLoss) * m.shakeHit / slRange
tpDst := (takeProfit - c) * m.shakeHit / slRange
if slDst > 0.5 && tpDst > 0.7 {
// 到止损的距离不低于50%
err := s.OpenOrder(&strat.EnterReq{
Tag: tag,
Short: m.shakeHit < 0,
StopLoss: stopLoss,
TakeProfit: takeProfit,
})
//date := btime.ToDateStr(btime.TimeMS(), "")
//log.Info(fmt.Sprintf("%s %s open %s c:%.3f sl:%.3f, tp:%.3f", s.Symbols.Symbols,
// date, tag, c, stopLoss, takeProfit))
if err != nil {
log.Error("open fail", zap.Error(err))
}
}
}
}
},
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
// ma := ta.EMA(e.Close, 5)
rsi := ta.RSI(e.Close, 9)
atr := ta.ATR(e.High, e.Low, e.Close, lenAtr)
atrBase := ta.Lowest(ta.Highest(atr, lenAtr), baseAtrLen).Get(0)
atrRate := atr.Get(0) / atrBase
// 计算平均实体比率
o, h, l, c := e.Open.Get(0), e.High.Get(0), e.Low.Get(0), e.Close.Get(0)
ph, pl, pc := e.High.Get(1), e.Low.Get(1), e.Close.Get(1)
solidTop, solidBtm := max(o, c), min(o, c)
tRange, pRange := h-l, ph-pl
//tr := e.GetSeries("tRange")
//tr.Set(math.Sqrt(tRange * 100 / c))
//maTRate := ta.EMA(tr, 3)
solRate := e.Close.To("solRate", 0)
solRate.Set(math.Abs(o-c) / tRange)
maSRT := ta.EMA(solRate, 3).Get(0)
// 计算上下行比率
dnRate := e.Close.To("dnRate", 0)
dnRate.Set((l - pc) / pRange)
maDnRate := ta.EMA(dnRate, 5)
upRate := e.Close.To("upRate", 0)
upRate.Set((h - pc) / pRange)
maUpRate := ta.EMA(upRate, 5)
m, _ := s.More.(*TMore)
m.shakeScore = (1 - maSRT) // * maTRate.Get(0)
top := c + maUpRate.Get(0)*tRange
btm := c + maDnRate.Get(0)*tRange
edgeOff := (top - btm) * (1 - rangeRate) * 0.5
m.btmPrice = btm - edgeOff
m.topPrice = top + edgeOff
//m.maChg = (ma.Get(0) - ma.Get(1)) / pRange
topLineRate := (h - solidTop) / tRange
btmLineRate := (solidBtm - l) / tRange
lineDiff := math.Abs(topLineRate-btmLineRate) / max(topLineRate, btmLineRate)
rsiVal, pRsi := rsi.Get(0)-50, rsi.Get(1)-50
m.shakeOK = (top-btm)*rangeRate/c >= minProfitPct && lineDiff < 0.35 && maSRT < 0.3
m.shakeHit = 0
//log.Info(fmt.Sprintf("h: %.3f l:%.3f c:%.3f maSRT: %.3f upRate:%.3f dnRate:%.3f %v", h, l, c,
// maSRT.Get(0), maUpRate.Get(0), maDnRate.Get(0), m.shakeOK))
if math.Abs(rsiVal) > 20 || math.Abs(pRsi) > 20 {
// (ATR较大/反向影线占比大)和RSI都极端时,容易反转,禁止震荡开单
revLineRate := btmLineRate
if rsiVal > 0 {
revLineRate = topLineRate
}
if atrRate > 2.5 || atrRate > 1.3 && revLineRate > 0.75 {
m.shakeOK = false
}
}
},
OnBatchInfos: func(tf string, jobs map[string]*strat.JobEnv) {
// 筛选出所有币中shakeScore降序的前n个,然后在子周期中低买高卖
var mores = make([]*TMore, 0, len(jobs))
for _, job := range jobs {
m, _ := job.Job.More.(*TMore)
mores = append(mores, m)
}
var shakeTopN = int(math.Ceil(float64(len(jobs)) * ShakeTop))
sort.Slice(mores, func(i, j int) bool {
return mores[i].shakeScore > mores[j].shakeScore
})
for _, m := range mores {
if shakeTopN > 0 && m.shakeOK {
shakeTopN -= 1
} else {
m.shakeOK = false
}
}
},
}
}