196 lines
4.9 KiB
Go
196 lines
4.9 KiB
Go
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
|
||
}
|