init
This commit is contained in:
172
fac/best.go
Normal file
172
fac/best.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user