This commit is contained in:
2026-08-24 20:53:21 +08:00
commit 6e78bba16a
287 changed files with 32443 additions and 0 deletions

172
fac/best.go Normal file
View File

@@ -0,0 +1,172 @@
package fac
import (
"github.com/banbox/banbot/config"
"github.com/banbox/banbot/opt"
"github.com/banbox/banbot/utils"
"github.com/banbox/banexg/log"
utils2 "github.com/banbox/banexg/utils"
"go.uber.org/zap"
"gonum.org/v1/gonum/floats"
)
func init() {
opt.FactorMap["best"] = best
opt.FactorMap["best3"] = best3
opt.FactorMap["bigVol"] = bigVols
opt.FactorMap["sharpe"] = sharpe
}
func bigVols(arg opt.FacArgs) ([]string, error) {
allNum := len(arg.Pairs)
cutNum := min(int(float64(allNum)/10), 30)
// 只取成交量高的一半品种
volPeriodMS := int64(utils2.TFToSecs("30d") * 1000)
volBarNum := int(volPeriodMS / arg.TFMSecs)
vols := make([]float64, 0, allNum)
for _, sta := range arg.Pairs {
start := max(0, len(sta.KLines)-volBarNum)
bars := sta.KLines[start:]
volSum := float64(0)
for _, b := range bars {
volSum += b.Volume * b.Close
}
vols = append(vols, volSum)
}
volIdxArr := utils.ArgSortDesc(vols)[:allNum/2]
var result = make([]string, 0, cutNum)
for _, idx := range volIdxArr {
result = append(result, arg.Pairs[idx].Symbol)
if len(result) >= cutNum {
break
}
}
return result, nil
}
func best(arg opt.FacArgs) ([]string, error) {
return bestOf(arg, 0)
}
func best3(arg opt.FacArgs) ([]string, error) {
return bestOf(arg, 0.25)
}
func bestOf(arg opt.FacArgs, offRate float64) ([]string, error) {
allNum := len(arg.Pairs)
initFund := float64(0)
for _, v := range config.WalletAmounts {
initFund = v
break
}
cutNum := min(int(float64(allNum)/10), 30)
// 只取成交量高的一半品种
volPeriodMS := int64(utils2.TFToSecs("30d") * 1000)
volBarNum := int(volPeriodMS / arg.TFMSecs)
vols := make([]float64, 0, allNum)
scores := make([]float64, 0, allNum)
for _, sta := range arg.Pairs {
start := max(0, len(sta.KLines)-volBarNum)
bars := sta.KLines[start:]
volSum := float64(0)
for _, b := range bars {
volSum += b.Volume * b.Close
}
vols = append(vols, volSum)
profitPct := floats.Sum(sta.Returns) * 100 / initFund
reals := make([]float64, 0, len(sta.Returns))
curFund := initFund
for _, v := range sta.Returns {
curFund += v
reals = append(reals, curFund)
}
drawRate, _ := utils.CalcDrawDown(reals, opt.ShowNum)
score := opt.CalcBtScore(profitPct, drawRate*100)
scores = append(scores, score)
}
volIdxArr := utils.ArgSortDesc(vols)[:allNum/2]
scoreIdxArr := utils.ArgSortDesc(scores)
volIdMap := utils.ArrToMap(volIdxArr, func(t2 int) int {
return t2
})
offset := int(float64(len(scoreIdxArr)) * offRate)
var result = make([]string, 0, cutNum)
for _, idx := range scoreIdxArr[offset:] {
if _, ok := volIdMap[idx]; ok {
result = append(result, arg.Pairs[idx].Symbol)
if len(result) >= cutNum {
break
}
}
}
return result, nil
}
func sharpe(arg opt.FacArgs) ([]string, error) {
allNum := len(arg.Pairs)
initFund := float64(0)
for _, v := range config.WalletAmounts {
initFund = v
break
}
cutNum := min(int(float64(allNum)/10), 30)
// 只取成交量高的一半品种
volPeriodMS := int64(utils2.TFToSecs("30d") * 1000)
volBarNum := int(volPeriodMS / arg.TFMSecs)
vols := make([]float64, 0, allNum)
scores := make([]float64, 0, allNum)
totOdNum := 0
rangeSecs := (arg.EndMS - arg.StartMS) / 1000
for _, sta := range arg.Pairs {
if len(sta.KLines) == 0 || sta.KLines[0].Time > arg.StartMS {
// 跳过回顾周期内数据不全的品种
vols = append(vols, 0)
scores = append(scores, -99)
continue
}
start := max(0, len(sta.KLines)-volBarNum)
bars := sta.KLines[start:]
volSum := float64(0)
for _, b := range bars {
volSum += b.Volume * b.Close
}
vols = append(vols, volSum)
reals := make([]float64, 0, len(sta.Returns))
curFund := initFund
for _, v := range sta.Returns {
curFund += v
reals = append(reals, curFund)
}
sharp, _, err := opt.CalcMeasuresByReal(reals, rangeSecs, "", 0, 0)
if err != nil {
return nil, err
}
scores = append(scores, sharp)
totOdNum += len(sta.Orders)
}
validNum := len(vols)
volIdxArr := utils.ArgSortDesc(vols)[:validNum/2]
scoreIdxArr := utils.ArgSortDesc(scores)
volIdMap := utils.ArrToMap(volIdxArr, func(t2 int) int {
return t2
})
avgOdNum := totOdNum / len(arg.Pairs)
var result = make([]string, 0, cutNum)
var raw = make([]string, 0, allNum)
for _, idx := range scoreIdxArr {
//raw[arg.Pairs[idx].Symbol] = scores[idx]
raw = append(raw, arg.Pairs[idx].Symbol)
if _, ok := volIdMap[idx]; ok {
sta := arg.Pairs[idx]
if len(sta.Orders)*4 < avgOdNum {
continue
}
result = append(result, sta.Symbol)
if len(result) >= cutNum {
break
}
}
}
log.Info("by sharpe", zap.Strings("pairs", raw))
return result, nil
}