183 lines
4.5 KiB
Go
183 lines
4.5 KiB
Go
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
|
||
}
|