package ent import ( "fmt" "github.com/banbox/banbot/biz" "github.com/banbox/banbot/btime" "github.com/banbox/banbot/config" "github.com/banbox/banbot/core" "github.com/banbox/banbot/orm" "github.com/banbox/banbot/utils" "github.com/banbox/banexg" "github.com/banbox/banexg/errs" utils2 "github.com/banbox/banexg/utils" "github.com/banbox/banstagy/com" "github.com/banbox/banta" "math" "path/filepath" "sort" "strconv" ) type chgSta struct { env *banta.BarEnv volMa *com.Ema chg float64 // 变动百分比 stability float64 // 稳定性分数 adx float64 rsi float64 er float64 price float64 weight float64 nexts []*orm.InfoKline num int } func maxChgTest() *errs.Error { exgId := "binance" tf := "1m" maAlpha := 0.03 viewNextNum := 5 tfMSecs := int64(utils2.TFToSecs(tf) * 1000) exsMap := orm.GetExSymbols(exgId, banexg.MarketLinear) exsList := make([]*orm.ExSymbol, 0, len(exsMap)) envs := make(map[string]*chgSta) staList := make([]*chgSta, 0, len(exsMap)) topStas := make([]*chgSta, 0, len(exsMap)/4) for _, exs := range exsMap { _, quote, _, _ := core.SplitSymbol(exs.Symbol) if quote != "USDT" { continue } exsList = append(exsList, exs) env := &chgSta{ env: &banta.BarEnv{ TimeFrame: tf, TFMSecs: tfMSecs, Exchange: exs.Exchange, MarketType: exs.Market, Symbol: exs.Symbol, MaxCache: 1000, }, volMa: com.NewEMA(maAlpha), } envs[exs.Symbol] = env staList = append(staList, env) } barNum := 0 barTimeMS := int64(0) var rows [][]string var head = []string{"time", "code", "chg", "weight", "price", "adx", "rsi", "er"} for i := 0; i < viewNextNum; i++ { head = append(head, "p"+strconv.Itoa(i+1)) } rows = append(rows, head) updateWeights := func() { total := float64(0) for _, e := range envs { total += e.volMa.Val } if math.IsNaN(total) || total == 0 { return } for _, e := range envs { e.weight = (e.volMa.Val / total) * 100 } sort.Slice(staList, func(i, j int) bool { return staList[i].weight > staList[j].weight }) topStas = staList[:150] } checkBatch := func() { sort.Slice(topStas, func(i, j int) bool { return math.Abs(topStas[i].chg) > math.Abs(topStas[j].chg) }) for i := 0; i < 3; i++ { e := topStas[i] row := []string{ btime.ToDateStr(barTimeMS, core.DefaultDateFmt), e.env.Symbol, strconv.FormatFloat(e.chg, 'f', 2, 64), strconv.FormatFloat(e.weight, 'f', 2, 64), fmt.Sprintf("%.3e", e.price), strconv.FormatFloat(e.adx, 'f', 2, 64), strconv.FormatFloat(e.rsi, 'f', 2, 64), strconv.FormatFloat(e.er, 'f', 2, 64), } for j := 0; j < viewNextNum; j++ { rate := (e.nexts[j].Close/e.price - 1) * 100 row = append(row, strconv.FormatFloat(rate, 'f', 2, 64)) } rows = append(rows, row) } } totalSecs := (config.TimeRange.EndMS - config.TimeRange.StartMS) / 1000 pbar := utils.NewPrgBar(int(totalSecs), "Run") defer pbar.Close() onBar := func(b *orm.InfoKline, nexts []*orm.InfoKline) { if b.Time > barTimeMS { if !b.IsWarmUp && barTimeMS > 0 { pbar.Add(int((b.Time - barTimeMS) / 1000)) } barNum += 1 barTimeMS = b.Time if barNum%20 == 1 { // 每隔20个bar,更新全局权重 updateWeights() } if !b.IsWarmUp && barNum%5 == 0 { // 每隔5个bar,计算一次变化 checkBatch() } } sta := envs[b.Symbol] sta.num += 1 e := sta.env e.OnBar(b.Time, b.Open, b.High, b.Low, b.Close, b.Volume, b.Quote, b.BuyVolume, b.TradeNum) avgPrice := (b.High + b.Low + b.Close) / 3 sta.volMa.Update(b.Volume * avgPrice) sta.chg = (b.Close/b.Open - 1) * 100 sta.adx = banta.ADX(e.High, e.Low, e.Close, 3).Get(0) sta.rsi = banta.RSI(e.Close, 3).Get(0) sta.er = banta.ER(e.Close, 3).Get(0) if b.IsWarmUp { return } sta.nexts = nexts sta.price = b.Close } var replayErr *errs.Error verCh := make(chan int, 1) onData := func(evt *orm.DataSeries, nexts []*orm.DataSeries) { bar, err_ := orm.AsKline(evt) if err_ != nil { replayErr = errs.New(errs.CodeRunTime, err_) select { case verCh <- -1: default: } return } futureBars := make([]*orm.InfoKline, 0, len(nexts)) for _, next := range nexts { futureBar, convErr := orm.AsKline(next) if convErr != nil { replayErr = errs.New(errs.CodeRunTime, convErr) select { case verCh <- -1: default: } return } futureBars = append(futureBars, futureBar) } onBar(bar, futureBars) } err := biz.RunHistSeries(&biz.RunHistSeriesArgs{ ExsList: exsList, Start: config.TimeRange.StartMS, End: config.TimeRange.EndMS, ViewNextNum: viewNextNum, TfWarms: map[string]int{tf: 100}, VerCh: verCh, OnData: onData, }) if err == nil && replayErr != nil { err = replayErr } outPath := filepath.Join(config.GetDataDir(), "dump", "maxChg.csv") err2 := utils.WriteCsvFile(outPath, rows, false) if err == nil { err = err2 } return err }