package ent import ( "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" "github.com/banbox/banstagy/com" "math" "path/filepath" "sort" "strconv" ) type volSta struct { code string longVol *com.Ema shortVol *com.Ema longRate float64 shortRate float64 weight float64 price float64 nexts []*orm.InfoKline num int } func maxVolTest() *errs.Error { exgId := "binance" tf := "1h" maAlpha := 0.03 viewNextNum := 3 exsMap := orm.GetExSymbols(exgId, banexg.MarketLinear) exsList := make([]*orm.ExSymbol, 0, len(exsMap)) envs := make(map[string]*volSta) envList := make([]*volSta, 0, len(exsMap)) for _, exs := range exsMap { _, quote, _, _ := core.SplitSymbol(exs.Symbol) if quote != "USDT" { continue } exsList = append(exsList, exs) env := &volSta{ code: exs.Symbol, longVol: com.NewEMA(maAlpha), shortVol: com.NewEMA(maAlpha), } envs[exs.Symbol] = env envList = append(envList, env) } barNum := 0 barTimeMS := int64(0) updateWeights := func() { total := float64(0) for _, e := range envs { total += e.longVol.Val + e.shortVol.Val } if math.IsNaN(total) { return } for _, e := range envs { wei := (e.longVol.Val + e.shortVol.Val) / total e.weight = math.Pow(wei, 0.5) } } var rows [][]string var head = []string{"time", "code", "longRt", "shortRt"} for i := 0; i < viewNextNum; i++ { head = append(head, "p"+strconv.Itoa(i+1)) } rows = append(rows, head) checkBatch := func() { sort.Slice(envList, func(i, j int) bool { return envList[i].longRate > envList[j].shortRate }) for i := 0; i < 3; i++ { e := envList[i] row := []string{ btime.ToDateStr(barTimeMS, core.DefaultDateFmt), e.code, strconv.FormatFloat(e.longRate, 'f', 3, 64), strconv.FormatFloat(e.shortRate, 'f', 3, 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 lineTot := (b.High - b.Low) - math.Abs(b.Open-b.Close) total := (b.High - b.Low) + lineTot + core.AmtDust longTot := lineTot if b.Open < b.Close { longTot += math.Abs(b.Open - b.Close) } avgPrice := (b.High + b.Low + b.Close) / 3 quoteVol := avgPrice * b.Volume longRt := longTot / total longVol := quoteVol * longRt shortVol := quoteVol - longVol sta.longVol.Update(longVol) sta.shortVol.Update(shortVol) if b.IsWarmUp { return } // 暂时不考虑多空直接比例 sta.longRate = longVol / sta.longVol.Val * sta.weight * math.Pow(longRt, 2) sta.shortRate = shortVol / sta.shortVol.Val * sta.weight * math.Pow(1-longRt, 2) 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", "moneyFlow.csv") err2 := utils.WriteCsvFile(outPath, rows, false) if err == nil { err = err2 } return err }