Files
banstagy/ent/max_chg.go
2026-08-24 20:53:21 +08:00

196 lines
4.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}