init
This commit is contained in:
271
arb/stat_job.go
Normal file
271
arb/stat_job.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package arb
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/btime"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banexg/log"
|
||||
"github.com/banbox/banexg/utils"
|
||||
ta "github.com/banbox/banta"
|
||||
"go.uber.org/zap"
|
||||
"maps"
|
||||
"math"
|
||||
"math/rand"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func init() {
|
||||
strat.AddStratGroup("arb", map[string]strat.FuncMakeStrat{
|
||||
"gp": ArbGroupJob,
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
strengths widRatios
|
||||
rate: 0 -0.002491 0.859934
|
||||
rate: 14234 -0.000915 0.946673
|
||||
rate: 28468 -0.000089 0.997275
|
||||
rate: 42702 0.000818 1.048731
|
||||
rate: 56936 0.002571 1.166192
|
||||
rate: 71170 0.007989 1.619902
|
||||
*/
|
||||
|
||||
func ArbGroupJob(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
lenFast := 6
|
||||
lenSlow := 20
|
||||
lenMid := 96
|
||||
lenStd := 96
|
||||
bollWid := 0.0043
|
||||
lastSplitMS := int64(0)
|
||||
tfMSecs := int64(60000)
|
||||
dayMS := int64(utils.TFToSecs("1d") * 1000)
|
||||
env, _ := ta.NewBarEnv(core.ExgName, core.Market, "", "1m")
|
||||
var rate *ta.Series
|
||||
groups := make(map[*strat.StratJob]float64)
|
||||
hits := make(map[string]float64)
|
||||
warmNum := 120
|
||||
var widths []float64
|
||||
var maxRate float64
|
||||
var position float64
|
||||
var posTag string
|
||||
var rates []*TimeFlt
|
||||
dumpDir := filepath.Join(config.GetDataDir(), "dump")
|
||||
return &strat.TradeStrat{
|
||||
WarmupNum: warmNum,
|
||||
RunTimeFrames: []string{"1m"},
|
||||
BatchInOut: true,
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
var curMS int64
|
||||
newNum := 0
|
||||
hitDup := maps.Clone(hits)
|
||||
for _, j := range jobs {
|
||||
curMS = max(curMS, j.Env.TimeStop)
|
||||
if _, ok := hitDup[j.Symbol.Symbol]; ok {
|
||||
delete(hitDup, j.Symbol.Symbol)
|
||||
} else {
|
||||
newNum += 1
|
||||
}
|
||||
}
|
||||
alignDayMS := utils.AlignTfMSecs(curMS, dayMS)
|
||||
if alignDayMS > lastSplitMS || newNum > 0 || len(hitDup) > 0 {
|
||||
// 每天刷新品种分组
|
||||
for j := range groups {
|
||||
j.CloseOrders(&strat.ExitReq{Tag: "switch", Log: true})
|
||||
}
|
||||
offsetMS := alignDayMS - dayMS
|
||||
dateStr := btime.ToDateStr(offsetMS, "20060102")
|
||||
dumpPath := filepath.Join(dumpDir, dateStr+".html")
|
||||
if len(rates) > 1000 {
|
||||
log.Info("try dump rates", zap.String("date", dateStr), zap.Int("num", len(rates)))
|
||||
err := dumpRateChart(rates, "1m", dumpPath, dateStr)
|
||||
if err != nil {
|
||||
log.Error("dumpRateChart fail", zap.Error(err))
|
||||
}
|
||||
}
|
||||
rates = nil
|
||||
rand.Shuffle(len(jobs), func(i, j int) {
|
||||
jobs[i], jobs[j] = jobs[j], jobs[i]
|
||||
})
|
||||
env.Reset()
|
||||
rate = env.NewSeries(nil)
|
||||
groups = make(map[*strat.StratJob]float64)
|
||||
hits = make(map[string]float64)
|
||||
for i, j := range jobs {
|
||||
if i%2 == 0 {
|
||||
groups[j] = -1
|
||||
} else {
|
||||
groups[j] = 1
|
||||
}
|
||||
hits[j.Symbol.Symbol] = j.Env.Close.Get(lenMid)
|
||||
}
|
||||
env.TimeStop = curMS - int64(lenMid)*tfMSecs
|
||||
env.TimeStart = env.TimeStop - tfMSecs
|
||||
for i := warmNum; i >= 0; i-- {
|
||||
var price1, price2 float64
|
||||
var num1, num2 float64
|
||||
for j, tag := range groups {
|
||||
base := hits[j.Symbol.Symbol]
|
||||
ratec := j.Env.Close.Get(i) / base
|
||||
if tag < 0 {
|
||||
price1 += ratec
|
||||
num1 += 1
|
||||
} else {
|
||||
price2 += ratec
|
||||
num2 += 1
|
||||
}
|
||||
}
|
||||
price1 /= num1
|
||||
price2 /= num2
|
||||
env.TimeStart += tfMSecs
|
||||
env.TimeStop += tfMSecs
|
||||
rate.Append(price1 / price2)
|
||||
_ = ta.EMA(rate, lenFast)
|
||||
_ = ta.EMA(rate, lenSlow)
|
||||
maMid := ta.EMA(rate, lenMid).Get(0)
|
||||
dev := ta.StdDev(rate, lenStd)
|
||||
stdDev := ta.SMA(dev, lenStd).Get(0) * 1.7
|
||||
bollWidAvg := bollWid*maMid*0.5 + stdDev*0.5
|
||||
widths = append(widths, bollWidAvg)
|
||||
}
|
||||
lastSplitMS = alignDayMS
|
||||
}
|
||||
var price1, price2 float64
|
||||
var num1, num2 float64
|
||||
for j, tag := range groups {
|
||||
base := hits[j.Symbol.Symbol]
|
||||
ratec := j.Env.Close.Get(0) / base
|
||||
if tag < 0 {
|
||||
price1 += ratec
|
||||
num1 += 1
|
||||
} else {
|
||||
price2 += ratec
|
||||
num2 += 1
|
||||
}
|
||||
}
|
||||
price1 /= num1
|
||||
price2 /= num2
|
||||
curRate := price1 / price2
|
||||
env.TimeStart += tfMSecs
|
||||
env.TimeStop += tfMSecs
|
||||
rate.Append(curRate)
|
||||
rates = append(rates, &TimeFlt{
|
||||
Stamp: curMS,
|
||||
Val: curRate,
|
||||
})
|
||||
maFast := ta.EMA(rate, lenFast)
|
||||
maSlow := ta.EMA(rate, lenSlow) //66
|
||||
maMidS := ta.EMA(rate, lenMid)
|
||||
maMid := maMidS.Get(0)
|
||||
dev := ta.StdDev(rate, lenStd)
|
||||
stdDev := ta.SMA(dev, lenStd).Get(0) * 1.7
|
||||
bollWidAvg := bollWid*maMid*0.5 + stdDev*0.5
|
||||
widths = append(widths, bollWidAvg)
|
||||
zScore := (curRate - maMid) / bollWidAvg
|
||||
strength := maMid/maMidS.Get(warmNum) - 1
|
||||
widDelta := bollWidAvg - widths[len(widths)-60]
|
||||
widRatio := bollWidAvg / widths[len(widths)-warmNum]
|
||||
fastXSlow := maFast.Cross(maSlow)
|
||||
fastXMid := maFast.Cross(maMidS)
|
||||
|
||||
absStrength := math.Abs(strength)
|
||||
mkt := ""
|
||||
if absStrength > 0.01 || math.Abs(zScore) > 4 {
|
||||
// D: 结构性跳变,暂停交易
|
||||
mkt = "D"
|
||||
} else if widRatio > 1.5 && widDelta > 0 || bollWidAvg >= 0.008 {
|
||||
// C: 高波动突破
|
||||
mkt = "C"
|
||||
} else if absStrength >= 0.003 || widRatio > 1.3 && widDelta > 0 {
|
||||
// B: 趋势市场
|
||||
mkt = "B"
|
||||
} else if absStrength < 0.002 && widRatio < 1.2 && widDelta <= 0 {
|
||||
// A: 低波动区间
|
||||
mkt = "A"
|
||||
} else {
|
||||
//if absStrength >= 0.001 && absStrength < 0.002 && widRatio < 1.3
|
||||
// E: 低频弱趋势,中性,混合策略
|
||||
mkt = "E"
|
||||
}
|
||||
dirt := -1.0
|
||||
if strength > 0 {
|
||||
dirt = 1
|
||||
}
|
||||
if math.Abs(zScore) > maxRate*dirt {
|
||||
maxRate = zScore
|
||||
}
|
||||
allow1, allow2 := false, false
|
||||
if mkt == "A" {
|
||||
allow1 = true
|
||||
} else if mkt == "B" || mkt == "C" {
|
||||
// B明显趋势 或 C波动扩大方向明确
|
||||
allow2 = true
|
||||
} else if mkt == "E" {
|
||||
allow1, allow2 = true, true
|
||||
}
|
||||
openDirt := 0.0
|
||||
openTag := ""
|
||||
dateStr := btime.ToDateStr(curMS, core.DefaultDateFmt)
|
||||
if allow1 {
|
||||
expPft := math.Abs(curRate - maMid)
|
||||
if math.Abs(maxRate) >= 1.5 && expPft > 0.002 && float64(fastXSlow)*dirt == -1 {
|
||||
openDirt = -dirt
|
||||
openTag = "rev"
|
||||
log.Info("open rev", zap.String("date", dateStr), zap.Float64("z", zScore),
|
||||
zap.Float64("width", bollWidAvg), zap.Float64("widDelta", widDelta))
|
||||
}
|
||||
}
|
||||
if allow2 && float64(fastXMid)*dirt == 1 {
|
||||
if absStrength >= 0.003 || widRatio > 1.3 && widDelta > 0 {
|
||||
openDirt = dirt
|
||||
openTag = "follow"
|
||||
log.Info("open trend", zap.String("date", dateStr), zap.Float64("z", zScore),
|
||||
zap.Float64("width", bollWidAvg), zap.Float64("widDelta", widDelta),
|
||||
zap.Float64("strength", strength), zap.Float64("widRatio", widRatio))
|
||||
}
|
||||
}
|
||||
if position != openDirt && openDirt != 0 {
|
||||
if position != 0 {
|
||||
log.Info("close pos", zap.String("date", dateStr), zap.String("tag", "reverse"), zap.Float64("dirt", position))
|
||||
for j := range groups {
|
||||
j.CloseOrders(&strat.ExitReq{Tag: "reverse"})
|
||||
}
|
||||
}
|
||||
for j, tag := range groups {
|
||||
j.OpenOrder(&strat.EnterReq{
|
||||
Tag: openTag,
|
||||
Short: tag != openDirt,
|
||||
})
|
||||
}
|
||||
log.Info("open pos", zap.String("date", dateStr), zap.String("tag", openTag), zap.Float64("dirt", openDirt))
|
||||
position = openDirt
|
||||
posTag = openTag
|
||||
} else if position != 0 {
|
||||
exitTag := ""
|
||||
if posTag == "rev" {
|
||||
if position*dirt > 0 {
|
||||
exitTag = "tpRev"
|
||||
} else if math.Abs(zScore) >= 3 {
|
||||
exitTag = "slRev"
|
||||
}
|
||||
} else if posTag == "follow" {
|
||||
if position*dirt < 0 && (math.Abs(zScore) > 0.5 || widDelta < 0) {
|
||||
exitTag = "slFol"
|
||||
} else if position*dirt > 0 && math.Abs(zScore) >= 1 {
|
||||
exitTag = "tpFol"
|
||||
}
|
||||
}
|
||||
if exitTag != "" {
|
||||
log.Info("close pos", zap.String("date", dateStr), zap.String("tag", exitTag), zap.Float64("dirt", position))
|
||||
position = 0
|
||||
posTag = ""
|
||||
for j := range groups {
|
||||
j.CloseOrders(&strat.ExitReq{Tag: exitTag})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user