init
This commit is contained in:
124
ent/fund_rate.go
Normal file
124
ent/fund_rate.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/btime"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/exg"
|
||||
"github.com/banbox/banbot/orm"
|
||||
utils2 "github.com/banbox/banbot/utils"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/errs"
|
||||
"github.com/banbox/banexg/utils"
|
||||
"github.com/banbox/banstagy/com"
|
||||
"math"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
/*
|
||||
永续合约资金费率结算后一段时间,如果资金费率<-0.0035,未来5分钟有较大概率下降
|
||||
*/
|
||||
|
||||
type FRate struct {
|
||||
Time int64
|
||||
Data []*com.CodeVal
|
||||
}
|
||||
|
||||
func runFundEnter() *errs.Error {
|
||||
exgId := "binance"
|
||||
exchange, err := exg.GetWith(exgId, banexg.MarketLinear, banexg.MarketSwap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = orm.LoadMarkets(exchange, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dumpDir := filepath.Join(config.GetDataDir(), "dump")
|
||||
err_ := utils2.EnsureDir(dumpDir, 0755)
|
||||
if err_ != nil {
|
||||
return errs.New(errs.CodeIOWriteFail, err_)
|
||||
}
|
||||
dumpPath := filepath.Join(dumpDir, exgId+"_fundingRate.json")
|
||||
his := make(map[string]map[int64]float64)
|
||||
err_ = utils.ReadJsonFile(dumpPath, &his, utils.JsonNumDefault)
|
||||
if err_ != nil {
|
||||
return errs.New(errs.CodeIOReadFail, err_)
|
||||
}
|
||||
dates := make(map[int64]*FRate)
|
||||
for code, timeMap := range his {
|
||||
for dateMS, val := range timeMap {
|
||||
sta, ok := dates[dateMS]
|
||||
if !ok {
|
||||
sta = &FRate{Time: dateMS}
|
||||
dates[dateMS] = sta
|
||||
}
|
||||
sta.Data = append(sta.Data, &com.CodeVal{
|
||||
Code: code,
|
||||
Val: val,
|
||||
})
|
||||
}
|
||||
}
|
||||
arr := make([]*FRate, 0, len(dates))
|
||||
for _, it := range dates {
|
||||
arr = append(arr, it)
|
||||
}
|
||||
sort.Slice(arr, func(i, j int) bool {
|
||||
return arr[i].Time < arr[j].Time
|
||||
})
|
||||
// 按时间戳,对资金费率绝对值较大的,筛选K线并记录价格
|
||||
rows := make([][]string, 0)
|
||||
barNum := 20
|
||||
backOff := 5
|
||||
head := []string{"time", "code", "rate", "price"}
|
||||
for i := 0; i < barNum; i++ {
|
||||
sid := i - backOff
|
||||
if sid >= 0 {
|
||||
sid += 1
|
||||
}
|
||||
head = append(head, "p"+strconv.Itoa(sid))
|
||||
}
|
||||
rows = append(rows, head)
|
||||
for _, dt := range arr {
|
||||
sort.Slice(dt.Data, func(i, j int) bool {
|
||||
return math.Abs(dt.Data[i].Val) > math.Abs(dt.Data[j].Val)
|
||||
})
|
||||
startMS := dt.Time - int64(backOff)*60000
|
||||
for _, it := range dt.Data {
|
||||
if math.Abs(it.Val) < 0.0005 {
|
||||
break
|
||||
}
|
||||
exs, err := orm.GetExSymbol(exchange, it.Code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, bars, err := orm.GetOHLCV(exs, "1m", startMS, 0, barNum, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(bars) < barNum {
|
||||
continue
|
||||
}
|
||||
row := make([]string, 0, 4+barNum)
|
||||
initPrice := bars[backOff].Open
|
||||
row = append(row, btime.ToDateStr(dt.Time, core.DefaultDateFmt), it.Code,
|
||||
strconv.FormatFloat(it.Val, 'f', 6, 64),
|
||||
fmt.Sprintf("%.4e", initPrice))
|
||||
var pct float64
|
||||
for i, b := range bars {
|
||||
if i < backOff {
|
||||
pct = (1 - b.Open/initPrice) * 100
|
||||
} else {
|
||||
pct = (b.Close/initPrice - 1) * 100
|
||||
}
|
||||
row = append(row, strconv.FormatFloat(pct, 'f', 4, 64))
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
}
|
||||
outPath := filepath.Join(dumpDir, exgId+"_fr.csv")
|
||||
return utils2.WriteCsvFile(outPath, rows, false)
|
||||
}
|
||||
137
ent/his_sharpe.go
Normal file
137
ent/his_sharpe.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"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/opt"
|
||||
"github.com/banbox/banbot/orm"
|
||||
"github.com/banbox/banbot/orm/ormo"
|
||||
utils2 "github.com/banbox/banexg/utils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func hisSharpe(args []string) error {
|
||||
var configPaths config.ArrString
|
||||
var inPath string
|
||||
var sub = flag.NewFlagSet("fac", flag.ExitOnError)
|
||||
sub.Var(&configPaths, "config", "config path to use, Multiple -config options may be used")
|
||||
sub.StringVar(&inPath, "in", "", "path for all pairs orders e.g.: orders.gob")
|
||||
err_ := sub.Parse(args)
|
||||
if err_ != nil {
|
||||
return err_
|
||||
}
|
||||
core.SetRunMode(core.RunModeBackTest)
|
||||
err := biz.SetupComsExg(&config.CmdArgs{
|
||||
Configs: configPaths,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var startMs = config.TimeRange.StartMS
|
||||
var endMS = config.TimeRange.EndMS
|
||||
if len(config.StakeCurrency) == 0 {
|
||||
return errors.New("`stake_currency` in yml is required")
|
||||
}
|
||||
var tfSecs int
|
||||
var orders []*ormo.InOutOrder
|
||||
if inPath == "" {
|
||||
return errors.New("-in is required")
|
||||
} else {
|
||||
inPath = config.ParsePath(inPath)
|
||||
}
|
||||
orders, err = ormo.LoadOrdersGob(inPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var exsMap = make(map[int32]*orm.ExSymbol)
|
||||
var pairMap = make(map[string]int32)
|
||||
var totalCost float64
|
||||
var validOdNum int
|
||||
if len(orders) > 0 {
|
||||
startMs = orders[0].RealEnterMS()
|
||||
endMS = orders[0].RealExitMS()
|
||||
for _, od := range orders {
|
||||
curStart := od.RealEnterMS()
|
||||
if curStart < startMs && curStart > 0 {
|
||||
startMs = curStart
|
||||
}
|
||||
curEnd := od.RealExitMS()
|
||||
if curEnd > endMS {
|
||||
endMS = curEnd
|
||||
}
|
||||
tfSecs = max(tfSecs, utils2.TFToSecs(od.Timeframe))
|
||||
curCost := od.EnterCost()
|
||||
if curCost > 0 {
|
||||
totalCost += curCost
|
||||
validOdNum += 1
|
||||
}
|
||||
if _, ok := pairMap[od.Symbol]; !ok {
|
||||
exs, err := orm.GetExSymbolCur(od.Symbol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exsMap[exs.ID] = exs
|
||||
pairMap[od.Symbol] = exs.ID
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New("orders is empty")
|
||||
}
|
||||
if validOdNum == 0 {
|
||||
return errors.New("no valid orders")
|
||||
}
|
||||
if tfSecs == 0 {
|
||||
tfSecs = utils2.TFToSecs("1d")
|
||||
}
|
||||
tf := "1d"
|
||||
// 滚动测试因子
|
||||
rangeStart, _ := btime.ParseTimeMS("2022-11-19")
|
||||
rangeEnd, _ := btime.ParseTimeMS("2024-11-18")
|
||||
pairOrders, err := opt.CutOrdersInRange(orders, rangeStart, rangeEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pairOrders = map[string][]*ormo.InOutOrder{
|
||||
"WIF/USDT:USDT": pairOrders["WIF/USDT:USDT"],
|
||||
}
|
||||
pairInfos, err := opt.CalcPairStats(pairOrders, rangeStart, rangeEnd, tf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
initFund := config.WalletAmounts["USDT"]
|
||||
rangeSecs := (rangeEnd - rangeStart) / 1000
|
||||
for _, sta := range pairInfos {
|
||||
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 err
|
||||
}
|
||||
labels := make([]string, len(reals))
|
||||
for i := range reals {
|
||||
labels[i] = strconv.Itoa(i)
|
||||
}
|
||||
g := opt.Chart{
|
||||
Labels: labels,
|
||||
Datasets: []*opt.ChartDs{
|
||||
{
|
||||
Label: "pow(x, 1.5)",
|
||||
Data: reals,
|
||||
BorderColor: "rgb(75, 192, 192)",
|
||||
},
|
||||
},
|
||||
}
|
||||
g.DumpFile("D:/dump_pairs2.html")
|
||||
fmt.Printf("%s sharpe: %v", sta.Symbol, sharp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
76
ent/main.go
Normal file
76
ent/main.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/entry"
|
||||
"github.com/banbox/banexg/errs"
|
||||
"github.com/banbox/banexg/log"
|
||||
"github.com/banbox/banstagy/arb"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
entry.AddCommand("tool", &cobra.Command{
|
||||
Use: "hisSharpe",
|
||||
DisableFlagParsing: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return hisSharpe(args)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func RunMyEntry(args []string) *errs.Error {
|
||||
if len(args) == 0 {
|
||||
log.Warn("sub command is require for `my`")
|
||||
return nil
|
||||
}
|
||||
var ag = &config.CmdArgs{}
|
||||
var f = flag.NewFlagSet("web", flag.ExitOnError)
|
||||
f.StringVar(&ag.LogLevel, "level", "info", "log level")
|
||||
f.StringVar(&ag.TimeZone, "tz", "", "timezone")
|
||||
f.StringVar(&ag.DataDir, "datadir", "", "Path to data dir.")
|
||||
f.StringVar(&ag.ConfigData, "config-data", "", "yaml config string")
|
||||
f.Var(&ag.Configs, "config", "config path to use, Multiple -config options may be used")
|
||||
if args == nil {
|
||||
args = os.Args
|
||||
}
|
||||
err_ := f.Parse(args[1:])
|
||||
if err_ != nil {
|
||||
return errs.New(errs.CodeRunTime, err_)
|
||||
}
|
||||
|
||||
err := biz.SetupComsExg(&config.CmdArgs{
|
||||
LogLevel: ag.LogLevel,
|
||||
DataDir: ag.DataDir,
|
||||
TimeZone: ag.TimeZone,
|
||||
Configs: ag.Configs,
|
||||
ConfigData: ag.ConfigData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch args[0] {
|
||||
case "fundRate":
|
||||
err = runFundEnter()
|
||||
case "moneyFlow":
|
||||
err = maxVolTest()
|
||||
case "maxChg":
|
||||
err = maxChgTest()
|
||||
case "twoPairRatio":
|
||||
err = arb.GenTwoPairRatio()
|
||||
case "profTuple":
|
||||
err = arb.ProfTuples()
|
||||
case "tupleSpike":
|
||||
err = arb.FindTupleSpikes()
|
||||
case "statGroup":
|
||||
err = arb.StatGroupPairs()
|
||||
case "balance":
|
||||
err = showBalance()
|
||||
default:
|
||||
log.Warn("unknown sub command for `my`")
|
||||
}
|
||||
return err
|
||||
}
|
||||
195
ent/max_chg.go
Normal file
195
ent/max_chg.go
Normal file
@@ -0,0 +1,195 @@
|
||||
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
|
||||
}
|
||||
182
ent/max_vol.go
Normal file
182
ent/max_vol.go
Normal file
@@ -0,0 +1,182 @@
|
||||
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
|
||||
}
|
||||
31
ent/tools.go
Normal file
31
ent/tools.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/exg"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/errs"
|
||||
"github.com/banbox/banexg/utils"
|
||||
)
|
||||
|
||||
func showBalance() *errs.Error {
|
||||
var account string
|
||||
for acc := range config.Accounts {
|
||||
account = acc
|
||||
break
|
||||
}
|
||||
exchange := exg.Default
|
||||
res, err := exchange.FetchBalance(map[string]interface{}{
|
||||
banexg.ParamAccount: account,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resstr, err_ := utils.MarshalString(res.Info)
|
||||
if err_ != nil {
|
||||
return errs.New(errs.CodeMarshalFail, err_)
|
||||
}
|
||||
fmt.Println(resstr)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user