init
This commit is contained in:
165
old/eat.go
Normal file
165
old/eat.go
Normal file
@@ -0,0 +1,165 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user