init
This commit is contained in:
121
rpc_fea/aifea.go
Normal file
121
rpc_fea/aifea.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/btime"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/errs"
|
||||
"github.com/banbox/banexg/log"
|
||||
utils2 "github.com/banbox/banexg/utils"
|
||||
"github.com/banbox/banta"
|
||||
"go.uber.org/zap"
|
||||
"gonum.org/v1/gonum/mat"
|
||||
"math"
|
||||
)
|
||||
|
||||
type AIFeaState struct {
|
||||
cg *banta.CGraph
|
||||
cgBig *banta.CGraph
|
||||
matBig [3]*mat.Dense
|
||||
feasBig *mat.Dense
|
||||
rateBig float64
|
||||
avgCostBigs []float64 //大周期的成本价
|
||||
}
|
||||
|
||||
func pubAiFea(exsList []*orm.ExSymbol, req *biz.SubReq, rsp biz.FeaFeeder_SubFeaturesServer) error {
|
||||
var sample = int(req.Sample)
|
||||
if sample <= 0 {
|
||||
sample = 10
|
||||
}
|
||||
startDt := btime.ToDateStr(req.Start, core.DefaultDateFmt)
|
||||
log.Info("start ai fea2", zap.Int("pair", len(exsList)), zap.String("start", startDt),
|
||||
zap.Int("sample", sample))
|
||||
var seqNum = 150 // 特征序列长度
|
||||
var penNum = 120 // 笔矩阵长度
|
||||
var depth = 100 // 笔矩阵深度:100个深度
|
||||
tfSml, tfBig := "5m", "1h"
|
||||
tfMSecs := int64(utils2.TFToSecs(tfSml) * 1000)
|
||||
tfMSecsBig := int64(utils2.TFToSecs(tfBig) * 1000)
|
||||
|
||||
states := make(map[string]*AIFeaState)
|
||||
for _, exs := range exsList {
|
||||
states[exs.Symbol] = &AIFeaState{
|
||||
cg: &banta.CGraph{ParseLevel: banta.ParseLevelPen, MaxPen: penNum * 3 / 2},
|
||||
cgBig: &banta.CGraph{ParseLevel: banta.ParseLevelPen, MaxPen: penNum * 3 / 2},
|
||||
}
|
||||
}
|
||||
|
||||
task := &GenFeaTask{
|
||||
TfSml: tfSml,
|
||||
TfBig: tfBig,
|
||||
WarmSml: 3000,
|
||||
WarmBig: 3000,
|
||||
TfMSecs: tfMSecs,
|
||||
TfMSecsBig: tfMSecsBig,
|
||||
NextObsNum: 30,
|
||||
}
|
||||
|
||||
batch := NewBatchMap(len(exsList))
|
||||
|
||||
task.OnBar = func(e *banta.BarEnv, b *banexg.Kline, futs []*banexg.Kline) *errs.Error {
|
||||
//if !core.IsWarmUp {
|
||||
// log.Info("OnBar", zap.Int64("time", e.TimeStop))
|
||||
//}
|
||||
state := states[e.Symbol]
|
||||
state.cg.AddBar(e)
|
||||
state.cg.Parse()
|
||||
feas := onAiFeatures(e, seqNum)
|
||||
if e.BarNum%sample > 0 || len(state.cg.Pens) < penNum || state.rateBig == 0 || len(futs) == 0 {
|
||||
// 预热时futs为nil
|
||||
return nil
|
||||
}
|
||||
// 收集特征,发送到监听端
|
||||
minChgVal := banta.ATR(e.High, e.Low, e.Close, 30).Get(0) * 5
|
||||
futProbs := genFutProbsV1(futs, minChgVal, 0.4)
|
||||
mats, rate := GenFeaMats(state.cg, penNum, depth, false)
|
||||
avgCosts := avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
info := make([]float64, 0, 25)
|
||||
info = append(info, float64(b.Time/1000), b.Open, b.High, b.Low, b.Close, b.Volume,
|
||||
math.Log1p(rate), math.Log1p(state.rateBig))
|
||||
info = append(info, avgCosts...)
|
||||
info = append(info, state.avgCostBigs...)
|
||||
info = append(info, futProbs[0], futProbs[1], futProbs[2])
|
||||
return batch.Add(rsp, e, b.Time, map[string]*biz.NumArr{
|
||||
"time": matToNumArr(mats[0]),
|
||||
"buy": matToNumArr(mats[1]),
|
||||
"sell": matToNumArr(mats[2]),
|
||||
"feas": matToNumArr(feas),
|
||||
"time2": matToNumArr(state.matBig[0]),
|
||||
"buy2": matToNumArr(state.matBig[1]),
|
||||
"sell2": matToNumArr(state.matBig[2]),
|
||||
"feas2": matToNumArr(state.feasBig),
|
||||
"info": {Data: info, Shape: []int32{int32(len(info))}},
|
||||
})
|
||||
}
|
||||
task.OnInfoBar = func(e *banta.BarEnv, b *banexg.Kline) *errs.Error {
|
||||
//if !core.IsWarmUp {
|
||||
// log.Info("OnInfoBar", zap.Int64("time", e.TimeStop))
|
||||
//}
|
||||
state := states[e.Symbol]
|
||||
state.cgBig.AddBar(e)
|
||||
state.cgBig.Parse()
|
||||
state.feasBig = onAiFeatures(e, seqNum)
|
||||
state.avgCostBigs = avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
if len(state.cgBig.Pens) >= penNum {
|
||||
state.matBig, state.rateBig = GenFeaMats(state.cgBig, penNum, depth, false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
task.OnEnvEnd = func(bar *banexg.PairTFKline, adj *orm.AdjInfo) {
|
||||
if bar == nil {
|
||||
return
|
||||
}
|
||||
state := states[bar.Symbol]
|
||||
state.cg = &banta.CGraph{ParseLevel: banta.ParseLevelPen, MaxPen: penNum * 3 / 2}
|
||||
state.cgBig = &banta.CGraph{ParseLevel: banta.ParseLevelPen, MaxPen: penNum * 3 / 2}
|
||||
state.rateBig = 0
|
||||
}
|
||||
return pubFeaBase(exsList, req, task)
|
||||
}
|
||||
107
rpc_fea/aifea2.go
Normal file
107
rpc_fea/aifea2.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/btime"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/errs"
|
||||
"github.com/banbox/banexg/log"
|
||||
utils2 "github.com/banbox/banexg/utils"
|
||||
"github.com/banbox/banta"
|
||||
"go.uber.org/zap"
|
||||
"gonum.org/v1/gonum/mat"
|
||||
)
|
||||
|
||||
type AiFea2State struct {
|
||||
cg *banta.CGraph
|
||||
feasBig *mat.Dense
|
||||
avgCostBigs []float64 //大周期的成本价
|
||||
}
|
||||
|
||||
func pubAiFea2(exsList []*orm.ExSymbol, req *biz.SubReq, rsp biz.FeaFeeder_SubFeaturesServer) error {
|
||||
var sample = int(req.Sample)
|
||||
if sample <= 0 {
|
||||
sample = 10
|
||||
}
|
||||
startDt := btime.ToDateStr(req.Start, core.DefaultDateFmt)
|
||||
endDt := btime.ToDateStr(req.End, core.DefaultDateFmt)
|
||||
log.Info("start ai fea2", zap.Int("pair", len(exsList)), zap.String("start", startDt),
|
||||
zap.String("end", endDt), zap.Int("sample", sample))
|
||||
var seqNum = 150 // 特征序列长度
|
||||
var penNum = 80 // 笔矩阵长度
|
||||
var depth = 100 // 笔矩阵深度:100个深度
|
||||
tfSml, tfBig := "5m", "1h"
|
||||
tfMSecs := int64(utils2.TFToSecs(tfSml) * 1000)
|
||||
tfMSecsBig := int64(utils2.TFToSecs(tfBig) * 1000)
|
||||
|
||||
task := &GenFeaTask{
|
||||
TfSml: tfSml,
|
||||
TfBig: tfBig,
|
||||
WarmSml: 2000,
|
||||
WarmBig: 300,
|
||||
TfMSecs: tfMSecs,
|
||||
TfMSecsBig: tfMSecsBig,
|
||||
NextObsNum: 30,
|
||||
}
|
||||
|
||||
states := make(map[string]*AiFea2State)
|
||||
for _, exs := range exsList {
|
||||
states[exs.Symbol] = &AiFea2State{
|
||||
cg: &banta.CGraph{ParseLevel: banta.ParseLevelPen, MaxPen: penNum * 3 / 2},
|
||||
}
|
||||
}
|
||||
|
||||
batch := NewBatchMap(len(exsList))
|
||||
|
||||
task.OnBar = func(e *banta.BarEnv, b *banexg.Kline, futs []*banexg.Kline) *errs.Error {
|
||||
state := states[e.Symbol]
|
||||
state.cg.AddBar(e)
|
||||
state.cg.Parse()
|
||||
feas := onAiFeatures2(e, 30)
|
||||
if e.BarNum%sample > 0 || len(state.cg.Pens) < penNum || len(futs) == 0 || state.feasBig == nil {
|
||||
// 预热时futs为nil
|
||||
return nil
|
||||
}
|
||||
// 收集特征,发送到监听端
|
||||
minChgVal := banta.ATR(e.High, e.Low, e.Close, 30).Get(0) * 5
|
||||
futProbs := genFutProbsV1(futs, minChgVal, 0.4)
|
||||
mats, _ := GenPenMats2(state.cg, penNum, depth, false)
|
||||
avgCosts := avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
info := make([]float64, 0, 25)
|
||||
info = append(info, avgCosts...)
|
||||
info = append(info, state.avgCostBigs...)
|
||||
return batch.Add(rsp, e, b.Time, map[string]*biz.NumArr{
|
||||
"one": matToNumArr(mats[0]),
|
||||
"two": matToNumArr(mats[1]),
|
||||
"feas": matToNumArr(feas),
|
||||
"feas2": matToNumArr(state.feasBig),
|
||||
"bar": {
|
||||
Data: []float64{float64(b.Time / 1000), b.Open, b.High, b.Low, b.Close, b.Volume},
|
||||
Shape: []int32{6},
|
||||
},
|
||||
"y": {
|
||||
Data: []float64{futProbs[0], futProbs[1], futProbs[2]},
|
||||
Shape: []int32{3},
|
||||
},
|
||||
"info": {
|
||||
Data: info, Shape: []int32{int32(len(info))},
|
||||
},
|
||||
})
|
||||
}
|
||||
task.OnInfoBar = func(e *banta.BarEnv, b *banexg.Kline) *errs.Error {
|
||||
state := states[e.Symbol]
|
||||
state.feasBig = onAiFeatures2(e, seqNum)
|
||||
state.avgCostBigs = avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
return nil
|
||||
}
|
||||
task.OnEnvEnd = func(bar *banexg.PairTFKline, adj *orm.AdjInfo) {
|
||||
if bar == nil {
|
||||
return
|
||||
}
|
||||
state := states[bar.Symbol]
|
||||
state.cg = &banta.CGraph{ParseLevel: banta.ParseLevelPen, MaxPen: penNum * 3 / 2}
|
||||
}
|
||||
return pubFeaBase(exsList, req, task)
|
||||
}
|
||||
176
rpc_fea/aifea_test.go
Normal file
176
rpc_fea/aifea_test.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/btime"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/utils"
|
||||
"github.com/banbox/banexg/log"
|
||||
"go.uber.org/zap"
|
||||
"gonum.org/v1/gonum/mat"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"math"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDataClient(t *testing.T) {
|
||||
addr := "127.0.0.1:6789"
|
||||
conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
log.Error("connect grpc fail", zap.Error(err))
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
c := biz.NewFeaFeederClient(conn)
|
||||
ctx := context.Background()
|
||||
startMS := int64(1682899200000)
|
||||
endMS := int64(1682985600000)
|
||||
task := "aifea2"
|
||||
codes := []string{"1000BONK/USDT:USDT", "1000FLOKI/USDT:USDT", "1000PEPE/USDT:USDT", "1000RATS/USDT:USDT",
|
||||
"1000SATS/USDT:USDT", "ACE/USDT:USDT", "AGIX/USDT:USDT", "ALICE/USDT:USDT", "API3/USDT:USDT",
|
||||
"ARKM/USDT:USDT", "ARK/USDT:USDT", "AR/USDT:USDT", "ATA/USDT:USDT", "ATOM/USDT:USDT",
|
||||
"AVAX/USDT:USDT", "AXS/USDT:USDT", "BADGER/USDT:USDT", "BAKE/USDT:USDT", "BAT/USDT:USDT",
|
||||
"BEAMX/USDT:USDT", "BICO/USDT:USDT", "BNB/USDT:USDT", "BNT/USDT:USDT", "BNX/USDT:USDT",
|
||||
"BTC/USDT:USDT", "CKB/USDT:USDT", "COMBO/USDT:USDT", "COMP/USDT:USDT", "CTK/USDT:USDT",
|
||||
"DEFI/USDT:USDT", "DODOX/USDT:USDT", "DOT/USDT:USDT", "DUSK/USDT:USDT", "ETHW/USDT:USDT",
|
||||
"FET/USDT:USDT", "FLM/USDT:USDT", "FLOW/USDT:USDT", "FRONT/USDT:USDT", "GLMR/USDT:USDT",
|
||||
"GRT/USDT:USDT", "GTC/USDT:USDT", "HFT/USDT:USDT", "HIFI/USDT:USDT", "HIGH/USDT:USDT", "ID/USDT:USDT",
|
||||
"ILV/USDT:USDT", "JASMY/USDT:USDT", "JOE/USDT:USDT", "JUP/USDT:USDT", "KLAY/USDT:USDT",
|
||||
"KNC/USDT:USDT", "KSM/USDT:USDT", "LDO/USDT:USDT", "LINA/USDT:USDT", "LIT/USDT:USDT",
|
||||
"LOOM/USDT:USDT", "LPT/USDT:USDT", "LSK/USDT:USDT", "LTC/USDT:USDT", "LUNA2/USDT:USDT",
|
||||
"MAGIC/USDT:USDT", "MANA/USDT:USDT", "NEO/USDT:USDT", "OGN/USDT:USDT", "ORDI/USDT:USDT",
|
||||
"RVN/USDT:USDT", "SFP/USDT:USDT", "SSV/USDT:USDT", "STRAX/USDT:USDT", "STX/USDT:USDT",
|
||||
"TRB/USDT:USDT", "XLM/USDT:USDT", "XRP/USDT:USDT", "ZIL/USDT:USDT"}
|
||||
// codes = []string{"AGIX/USDT:USDT"} //, "API3/USDT:USDT", "ARKM/USDT:USDT"}
|
||||
matKeys := map[string]bool{"time": true, "buy": true, "sell": true, "one": true, "two": true,
|
||||
"feas": true, "feas2": true}
|
||||
staMap := map[string][]*ColSta{}
|
||||
logSta := func(key string, sta *ColSta) {
|
||||
items, ok := staMap[key]
|
||||
if !ok {
|
||||
items = make([]*ColSta, 0, 30)
|
||||
}
|
||||
items = append(items, sta)
|
||||
staMap[key] = items
|
||||
}
|
||||
res, err := c.SubFeatures(ctx, &biz.SubReq{
|
||||
Exchange: "binance",
|
||||
Market: "linear",
|
||||
Codes: codes,
|
||||
Start: startMS,
|
||||
End: endMS,
|
||||
Task: task,
|
||||
Sample: 1,
|
||||
})
|
||||
fmt.Printf("\nlisten for %s\n", codes)
|
||||
if err != nil {
|
||||
log.Error("call SubFeatures fail", zap.Error(err))
|
||||
return
|
||||
}
|
||||
for {
|
||||
fea, err := res.Recv()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "EOF") {
|
||||
log.Info("done", zap.Strings("code", codes))
|
||||
} else {
|
||||
log.Error("receive feature fail", zap.Strings("code", codes), zap.Error(err))
|
||||
}
|
||||
break
|
||||
}
|
||||
for key, item := range fea.Mats {
|
||||
if _, ok := matKeys[key]; ok {
|
||||
row, col := int(item.Shape[0]), int(item.Shape[1])
|
||||
arr := mat.NewDense(row, col, item.Data)
|
||||
for j := 0; j < col; j++ {
|
||||
column := make([]float64, row)
|
||||
for i := 0; i < row; i++ {
|
||||
column[i] = arr.At(i, j)
|
||||
}
|
||||
sta := sampleArr(column)
|
||||
if sta.Nan > 0 || sta.Min < -1 || sta.Max > 1 {
|
||||
fmt.Printf("\n%v col %v: %v", key, j, staToStr(sta))
|
||||
}
|
||||
logSta(fmt.Sprintf("%s_%v", key, j), sta)
|
||||
}
|
||||
} else {
|
||||
sta := sampleArr(item.Data)
|
||||
if sta.Nan > 0 || sta.Min < -1 || sta.Max > 1 {
|
||||
fmt.Printf("\n%v: %v", key, staToStr(sta))
|
||||
}
|
||||
logSta(key, sta)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
keys := utils.KeysOfMap(staMap)
|
||||
slices.Sort(keys)
|
||||
fmt.Printf("\n\n\n sta cross all codes:\n")
|
||||
for _, key := range keys {
|
||||
items, _ := staMap[key]
|
||||
nan := 0
|
||||
sum, minVal, maxVal := float64(0), math.MaxFloat64, -math.MaxFloat64
|
||||
for _, it := range items {
|
||||
sum += it.Mean
|
||||
nan += it.Nan
|
||||
minVal = min(minVal, it.Min)
|
||||
maxVal = max(maxVal, it.Max)
|
||||
}
|
||||
sta := &ColSta{Nan: nan, Min: minVal, Max: maxVal, Mean: sum / float64(len(items))}
|
||||
if sta.Nan > 0 || sta.Min < -1 || sta.Max > 1 {
|
||||
fmt.Printf("\n%v: %v", key, staToStr(sta))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAiFea2(t *testing.T) {
|
||||
addr := "127.0.0.1:6789"
|
||||
conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
log.Error("connect grpc fail", zap.Error(err))
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
c := biz.NewFeaFeederClient(conn)
|
||||
ctx := context.Background()
|
||||
startMS := int64(1682899200000)
|
||||
endMS := int64(1682985600000)
|
||||
task := "aifea2"
|
||||
code := "AGIX/USDT:USDT"
|
||||
res, err := c.SubFeatures(ctx, &biz.SubReq{
|
||||
Exchange: "binance",
|
||||
Market: "linear",
|
||||
Codes: []string{code},
|
||||
Start: startMS,
|
||||
End: endMS,
|
||||
Task: task,
|
||||
Sample: 1,
|
||||
})
|
||||
fmt.Printf("\nlisten for %s\n", code)
|
||||
if err != nil {
|
||||
log.Error("call SubFeatures fail", zap.Error(err))
|
||||
return
|
||||
}
|
||||
for {
|
||||
fea, err := res.Recv()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "EOF") {
|
||||
log.Info("done", zap.String("code", code))
|
||||
} else {
|
||||
log.Error("receive feature fail", zap.String("code", code), zap.Error(err))
|
||||
}
|
||||
break
|
||||
}
|
||||
a := staToStr(sampleArr(fea.Mats["one"].Data))
|
||||
b := staToStr(sampleArr(fea.Mats["two"].Data))
|
||||
cc := staToStr(sampleArr(fea.Mats["feas"].Data))
|
||||
d := staToStr(sampleArr(fea.Mats["feas2"].Data))
|
||||
bar := fea.Mats["bar"].Data
|
||||
curDate := btime.ToDateStr(int64(bar[0]), core.DefaultDateFmt)
|
||||
fmt.Printf("%s, %.5f, %s, %s, %s, %s\n", curDate, bar[4], a, b, cc, d)
|
||||
}
|
||||
}
|
||||
745
rpc_fea/common.go
Normal file
745
rpc_fea/common.go
Normal file
@@ -0,0 +1,745 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/utils"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/errs"
|
||||
ta "github.com/banbox/banta"
|
||||
"gonum.org/v1/gonum/floats"
|
||||
"gonum.org/v1/gonum/mat"
|
||||
"math"
|
||||
"slices"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func genFutProbsV1(arr []*banexg.Kline, minChgVal float64, maxRevRate float64) [3]float64 {
|
||||
first, upper, lower, chgVal := getFutBounds(arr)
|
||||
total := upper - lower
|
||||
chgRate := chgVal / first
|
||||
upRate := (upper - first) / total
|
||||
dnRate := (first - lower) / total
|
||||
revRate := min(upRate, dnRate) / max(upRate, dnRate)
|
||||
var zeroRate float64
|
||||
if revRate > maxRevRate {
|
||||
// 反向波动过大,视为不确定
|
||||
zeroRate = min(1.0, max(0.55, revRate+0.2))
|
||||
} else if chgRate < 0.005 || chgVal < minChgVal {
|
||||
// 波动太小,视为不确定
|
||||
var rateScore, valScore float64
|
||||
if chgRate < 0.01 {
|
||||
rateScore = 1 - chgRate/0.01
|
||||
}
|
||||
if chgVal < minChgVal*2 {
|
||||
valScore = 1 - chgVal/minChgVal/2
|
||||
}
|
||||
zeroRate = max(0.55, rateScore*0.5+valScore*0.5)
|
||||
} else {
|
||||
// 有方向的行情,将反向波动50%作为不确定的概率
|
||||
zeroRate = revRate * 0.5
|
||||
}
|
||||
dirtRate := 1 - zeroRate
|
||||
upRate *= dirtRate
|
||||
dnRate *= dirtRate
|
||||
return [3]float64{zeroRate, upRate, dnRate}
|
||||
}
|
||||
|
||||
/*
|
||||
genFutProbs
|
||||
生成未来行情概率:[波动幅度,上升,下降,类别]
|
||||
*/
|
||||
func genFutProbs(arr []*banexg.Kline, minChgVal float64) [4]float64 {
|
||||
first, upper, lower, chgVal := getFutBounds(arr)
|
||||
total := upper - lower
|
||||
chgRate := chgVal / first
|
||||
upRate := (upper - first) / total
|
||||
dnRate := (first - lower) / total
|
||||
var cate float64
|
||||
if min(upRate, dnRate)/max(upRate, dnRate) < 0.4 {
|
||||
if upRate > dnRate {
|
||||
cate = 1
|
||||
} else {
|
||||
cate = 2
|
||||
}
|
||||
}
|
||||
if chgVal < minChgVal {
|
||||
// 低于最低限度,置为高低相同
|
||||
upRate, dnRate = 0.5, 0.5
|
||||
cate = 0
|
||||
} else if chgVal < minChgVal*2 {
|
||||
// 不够高,逐渐调整
|
||||
overRate := (chgVal - minChgVal) / minChgVal // 这是保持概率
|
||||
sameRate := (1 - overRate) / 2 // 这是拉平的概率
|
||||
upRate = upRate*overRate + sameRate
|
||||
dnRate = dnRate*overRate + sameRate
|
||||
if cate != 0 && min(upRate, dnRate)/max(upRate, dnRate) > 0.6 {
|
||||
cate = 0
|
||||
}
|
||||
}
|
||||
// chgRate的30个5m变化率平均是1%,则乘以100
|
||||
return [4]float64{math.Log1p(chgRate*100) / 2, upRate, dnRate, cate}
|
||||
}
|
||||
|
||||
func getFutBounds(arr []*banexg.Kline) (float64, float64, float64, float64) {
|
||||
var first = arr[0].Open
|
||||
var lower, upper = first, first
|
||||
var lowAt, uppAt = 0, 0
|
||||
for i, b := range arr {
|
||||
if b.Low < lower {
|
||||
lowAt = i
|
||||
lower = b.Low
|
||||
}
|
||||
if b.High > upper {
|
||||
uppAt = i
|
||||
upper = b.High
|
||||
}
|
||||
}
|
||||
var chgVal float64
|
||||
if upper-first > first-lower {
|
||||
// 属于上升趋势
|
||||
arr = arr[:uppAt+1]
|
||||
lower = first
|
||||
for _, b := range arr {
|
||||
lower = min(lower, b.Low)
|
||||
}
|
||||
chgVal = upper - first
|
||||
} else {
|
||||
// 下降趋势
|
||||
arr = arr[:lowAt+1]
|
||||
upper = first
|
||||
for _, b := range arr {
|
||||
upper = max(upper, b.High)
|
||||
}
|
||||
chgVal = first - lower
|
||||
}
|
||||
return first, upper, lower, chgVal
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
计算给定周期内的成本价
|
||||
*/
|
||||
func avgCostN(e *ta.BarEnv, periods ...int) []float64 {
|
||||
maxPeriod := slices.Max(periods)
|
||||
opens := e.Open.Range(0, maxPeriod)
|
||||
highs := e.High.Range(0, maxPeriod)
|
||||
lows := e.Low.Range(0, maxPeriod)
|
||||
closes := e.Close.Range(0, maxPeriod)
|
||||
vols := e.Volume.Range(0, maxPeriod)
|
||||
slices.Sort(periods)
|
||||
price := closes[0]
|
||||
// 累计流入资金,累计流入成交量
|
||||
sumCost, sumVol := float64(0), float64(0)
|
||||
results := make([]float64, 0, len(periods))
|
||||
hitIdx := 0
|
||||
for i := 0; i < len(opens); i++ {
|
||||
if i >= periods[hitIdx] {
|
||||
results = append(results, sumCost/sumVol/price-1)
|
||||
hitIdx += 1
|
||||
}
|
||||
o, h, l, c, v := opens[i], highs[i], lows[i], closes[i], vols[i]
|
||||
barUp, barDn := max(o, c), min(o, c)
|
||||
total := h - l + (h - barUp) + (barDn - l)
|
||||
if total <= 0 {
|
||||
continue
|
||||
}
|
||||
// 净买入或净卖出的比例
|
||||
sideRate := (barUp - barDn) * 0.33 / total
|
||||
dirt := -1.0
|
||||
if o < c {
|
||||
dirt = 1.0
|
||||
}
|
||||
curVol := sideRate * v * dirt
|
||||
sumVol += curVol
|
||||
sumCost += curVol * (barUp*0.5 + barDn*0.5)
|
||||
}
|
||||
if len(results) < len(periods) && sumVol != 0 {
|
||||
results = append(results, sumCost/sumVol/price-1)
|
||||
}
|
||||
for len(results) < len(periods) {
|
||||
results = append(results, 0)
|
||||
}
|
||||
for i, v := range results {
|
||||
results[i] = cutRange(v, -1, 1, 0)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func matToNumArr(m *mat.Dense) *biz.NumArr {
|
||||
data := m.RawMatrix().Data
|
||||
row, col := m.Dims()
|
||||
return &biz.NumArr{Data: data, Shape: []int32{int32(row), int32(col)}}
|
||||
}
|
||||
|
||||
func onAiFeatures(e *ta.BarEnv, num int) *mat.Dense {
|
||||
volMa5 := ta.SMA(e.Volume, 5)
|
||||
volMa20 := ta.SMA(e.Volume, 20)
|
||||
volMa70 := ta.SMA(e.Volume, 70)
|
||||
vwma5 := ta.VWMA(e.Close, e.Volume, 5)
|
||||
vwma20 := ta.VWMA(e.Close, e.Volume, 20)
|
||||
vwma70 := ta.VWMA(e.Close, e.Volume, 70)
|
||||
rsi14 := ta.RSI(e.Close, 14)
|
||||
rsi50 := ta.RSI(e.Close, 50)
|
||||
atr14 := ta.ATR(e.High, e.Low, e.Close, 14)
|
||||
atr50 := ta.ATR(e.High, e.Low, e.Close, 30)
|
||||
sma5 := ta.SMA(e.Close, 5)
|
||||
sma20 := ta.SMA(e.Close, 20)
|
||||
sma70 := ta.SMA(e.Close, 70)
|
||||
macd, macdS := ta.MACD(e.Close, 12, 26, 9)
|
||||
macd2, macdS2 := ta.MACD(e.Close, 40, 90, 30)
|
||||
kdjK, kdjD, _ := ta.KDJ(e.High, e.Low, e.Close, 9, 3, 3)
|
||||
kdjK2, kdjD2, _ := ta.KDJ(e.High, e.Low, e.Close, 48, 16, 12)
|
||||
stdDev := ta.StdDev(e.Close, 10)
|
||||
adx := ta.ADXBy(e.High, e.Low, e.Close, 14, 0, 1)
|
||||
roc := ta.ROC(e.Close, 30)
|
||||
if num == 0 {
|
||||
return nil
|
||||
}
|
||||
// 这里的特征列数最好是4或10的倍数,否则模型端采样会有问题
|
||||
feaNum := 30
|
||||
res := mat.NewDense(num, feaNum, nil)
|
||||
for i := 0; i < num; i++ {
|
||||
closeVal := e.Close.Get(i)
|
||||
volVal := e.Volume.Get(i)
|
||||
volMa5Val := volMa5.Get(i)
|
||||
volMa20Val := volMa20.Get(i)
|
||||
sma5Val := sma5.Get(i)
|
||||
sma20Val := sma20.Get(i)
|
||||
vwma5Val := vwma5.Get(i)
|
||||
vwma20Val := vwma20.Get(i)
|
||||
row := []float64{
|
||||
e.High.Get(i),
|
||||
e.Low.Get(i),
|
||||
closeVal,
|
||||
volVal,
|
||||
math.Log1p(volVal/volMa5Val) / 3,
|
||||
math.Log1p(volMa5Val/volMa20Val) / 3, // 5
|
||||
math.Log1p(volMa20Val/volMa70.Get(i)) / 3,
|
||||
(closeVal - e.Close.Get(i+1)) / closeVal,
|
||||
(closeVal - e.Close.Get(i+7)) / closeVal, // 8 此行超出(-1,1),调用方需截断
|
||||
rsi14.Get(i) / 100,
|
||||
rsi50.Get(i) / 100, // 10
|
||||
atr14.Get(i) / closeVal,
|
||||
atr50.Get(i) / closeVal,
|
||||
sma5Val/closeVal - 1,
|
||||
sma20Val/sma5Val - 1,
|
||||
sma70.Get(i)/sma20Val - 1, // 15
|
||||
vwma5Val/closeVal - 1, // 16 此行超出(-1,1),调用方需截断
|
||||
vwma20Val/vwma5Val - 1, // 17 此行超出(-1,1),调用方需截断
|
||||
vwma70.Get(i)/vwma20Val - 1,
|
||||
macd.Get(i) / closeVal,
|
||||
(macd.Get(i) - macdS.Get(i)) / closeVal, // 20
|
||||
macd2.Get(i) / closeVal,
|
||||
(macd2.Get(i) - macdS2.Get(i)) / closeVal,
|
||||
kdjK.Get(i) / 100,
|
||||
(kdjK.Get(i) - kdjD.Get(i)) / 100,
|
||||
kdjK2.Get(i) / 100, // 25
|
||||
(kdjK2.Get(i) - kdjD2.Get(i)) / 100,
|
||||
stdDev.Get(i) / closeVal,
|
||||
adx.Get(i) / 100,
|
||||
roc.Get(i) / 100, // 29 此行超出(-1,1),调用方需截断
|
||||
}
|
||||
for j, v := range row {
|
||||
if math.IsNaN(v) {
|
||||
v = 0
|
||||
}
|
||||
res.Set(i, j, v)
|
||||
}
|
||||
}
|
||||
// 对前四列hlcv进行归一化
|
||||
for i := 0; i < 4; i++ {
|
||||
col := res.ColView(i)
|
||||
minVal := mat.Min(col)
|
||||
maxVal := mat.Max(col)
|
||||
if minVal == maxVal {
|
||||
continue
|
||||
}
|
||||
for row := 0; row < num; row++ {
|
||||
oldValue := res.At(row, i)
|
||||
newValue := (oldValue - minVal) / (maxVal - minVal)
|
||||
res.Set(row, i, newValue)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func onAiFeatures2(e *ta.BarEnv, num int) *mat.Dense {
|
||||
if num <= 10 {
|
||||
panic("onAiFeatures2.num must greate than 10")
|
||||
}
|
||||
volMa5 := ta.SMA(e.Volume, 5)
|
||||
volMa20 := ta.SMA(e.Volume, 20)
|
||||
volMa70 := ta.SMA(e.Volume, 70)
|
||||
vwma5 := ta.VWMA(e.Close, e.Volume, 5)
|
||||
vwma20 := ta.VWMA(e.Close, e.Volume, 20)
|
||||
vwma70 := ta.VWMA(e.Close, e.Volume, 70)
|
||||
rsi14 := ta.RSI(e.Close, 14)
|
||||
rsiMa := ta.SMA(rsi14, 5)
|
||||
rsi50 := ta.RSI(e.Close, 50)
|
||||
atr14 := ta.ATR(e.High, e.Low, e.Close, 14)
|
||||
atr50 := ta.ATR(e.High, e.Low, e.Close, 30)
|
||||
sma5 := ta.SMA(e.Close, 5)
|
||||
sma20 := ta.SMA(e.Close, 20)
|
||||
sma70 := ta.SMA(e.Close, 70)
|
||||
macd, macdS := ta.MACD(e.Close, 12, 26, 9)
|
||||
macd2, macdS2 := ta.MACD(e.Close, 40, 90, 30)
|
||||
kdjK, kdjD, _ := ta.KDJ(e.High, e.Low, e.Close, 9, 3, 3)
|
||||
kdjK2, kdjD2, _ := ta.KDJ(e.High, e.Low, e.Close, 48, 16, 12)
|
||||
stdDev := ta.StdDev(e.Close, 10)
|
||||
bbUp, _, bbDn := ta.BBANDS(e.Close, 15, 1.6, 1.6)
|
||||
bb2Up, _, bb2Dn := ta.BBANDS(e.Close, 30, 2.5, 2.5)
|
||||
aroonUp, aroonOsc, aroonDn := ta.Aroon(e.High, e.Low, 14)
|
||||
aroon2Up, aroon2Osc, aroon2Dn := ta.Aroon(e.High, e.Low, 30)
|
||||
adx := ta.ADX(e.High, e.Low, e.Close, 14)
|
||||
adxPlus, _ := ta.PluMinDI(e.High, e.Low, e.Close, 14)
|
||||
adx2 := ta.ADX(e.High, e.Low, e.Close, 50)
|
||||
adxPlus2, _ := ta.PluMinDI(e.High, e.Low, e.Close, 50)
|
||||
roc := ta.ROC(e.Close, 9)
|
||||
roc2 := ta.ROC(e.Close, 30)
|
||||
tnr := ta.ER(e.Close, 15)
|
||||
tnr2 := ta.ER(e.Close, 50)
|
||||
cci := ta.CCI(e.Close, 15)
|
||||
cci50 := ta.CCI(e.Close, 50)
|
||||
cci100 := ta.CCI(e.Close, 100)
|
||||
cmf := ta.CMF(e, 15)
|
||||
cmf50 := ta.CMF(e, 50)
|
||||
kama := ta.KAMA(e.Close, 15)
|
||||
kama30 := ta.KAMA(e.Close, 30)
|
||||
willr := ta.WillR(e, 15)
|
||||
willr30 := ta.WillR(e, 30)
|
||||
willr30_5 := ta.SMA(willr30, 5)
|
||||
willr50 := ta.WillR(e, 50)
|
||||
srsiK, srsiD := ta.StochRSI(e.Close, 14, 14, 3, 3)
|
||||
srsi2K, srsi2D := ta.StochRSI(e.Close, 30, 30, 5, 5)
|
||||
mfi := ta.MFI(e, 14)
|
||||
mfi30 := ta.MFI(e, 30)
|
||||
rmi := ta.RMI(e.Close, 14, 3)
|
||||
rmi30 := ta.RMI(e.Close, 30, 5)
|
||||
hh14 := ta.Highest(e.Close, 14)
|
||||
hh50 := ta.Highest(e.Close, 50)
|
||||
ll14 := ta.Lowest(e.Close, 14)
|
||||
ll50 := ta.Lowest(e.Close, 50)
|
||||
td := ta.TD(e.Close)
|
||||
// 这里的特征列数最好是4或10的倍数,否则模型端采样会有问题
|
||||
feaNum := 72
|
||||
arrData := make([]float64, 0, num*feaNum)
|
||||
offset := 0
|
||||
for i := 0; i < num; i++ {
|
||||
closeVal := e.Close.Get(i)
|
||||
volMa5Val := volMa5.Get(i)
|
||||
volMa20Val := volMa20.Get(i)
|
||||
sma20Val := sma20.Get(i)
|
||||
vwma5Val := vwma5.Get(i)
|
||||
vwma20Val := vwma20.Get(i)
|
||||
ll14Val := ll14.Get(i)
|
||||
hh14Val := hh14.Get(i)
|
||||
kdjKVal := kdjK.Get(i)
|
||||
kdjK2Val := kdjK2.Get(i)
|
||||
arrData = append(arrData,
|
||||
e.High.Get(i),
|
||||
e.Low.Get(i),
|
||||
closeVal,
|
||||
e.Volume.Get(i),
|
||||
math.Log1p(e.Volume.Get(i)/volMa5Val)/3,
|
||||
math.Log1p(volMa5Val/volMa20Val)/3, // 5
|
||||
math.Log1p(volMa20Val/volMa70.Get(i))/3,
|
||||
(closeVal-e.Close.Get(i+1))/closeVal,
|
||||
(closeVal-e.Close.Get(i+7))/closeVal, // 8 此行超出(-1,1),调用方需截断
|
||||
rsi14.Get(i)/100,
|
||||
rsi50.Get(i)/100, // 10
|
||||
(rsiMa.Get(i)-rsi14.Get(i))/100,
|
||||
atr14.Get(i)/closeVal,
|
||||
atr50.Get(i)/closeVal,
|
||||
sma5.Get(i)/closeVal-1,
|
||||
sma20Val/sma5.Get(i)-1, // 15
|
||||
sma70.Get(i)/sma20Val-1,
|
||||
vwma5Val/closeVal-1, // 17 此行超出(-1,1),调用方需截断
|
||||
vwma20Val/vwma5Val-1, // 18 此行超出(-1,1),调用方需截断
|
||||
vwma70.Get(i)/vwma20Val-1,
|
||||
macd.Get(i)/closeVal, // 20
|
||||
(macd.Get(i)-macdS.Get(i))/closeVal,
|
||||
macd2.Get(i)/closeVal,
|
||||
(macd2.Get(i)-macdS2.Get(i))/closeVal,
|
||||
kdjKVal/100,
|
||||
(kdjKVal-kdjD.Get(i))/100, // 25
|
||||
kdjK2Val/100,
|
||||
(kdjK2Val-kdjD2.Get(i))/100,
|
||||
stdDev.Get(i)/closeVal,
|
||||
adx.Get(i)/100,
|
||||
adxPlus.Get(i)/100, // 30
|
||||
adx2.Get(i)/100,
|
||||
adxPlus2.Get(i)/100,
|
||||
roc.Get(i)/100, // 33 此行超出(-1,1),调用方需截断
|
||||
roc2.Get(i)/100, // 34 此行超出(-1,1),调用方需截断
|
||||
(closeVal-bbUp.Get(i))/closeVal,
|
||||
(closeVal-bbDn.Get(i))/closeVal,
|
||||
(closeVal-bb2Up.Get(i))/closeVal,
|
||||
(closeVal-bb2Dn.Get(i))/closeVal,
|
||||
aroonUp.Get(i)/100,
|
||||
aroonOsc.Get(i)/100,
|
||||
aroonDn.Get(i)/100,
|
||||
aroon2Up.Get(i)/100,
|
||||
aroon2Osc.Get(i)/100,
|
||||
aroon2Dn.Get(i)/100,
|
||||
tnr.Get(i),
|
||||
tnr2.Get(i),
|
||||
cci.Get(i)/400, // 47 超出1,需截断
|
||||
cci50.Get(i)/400, // 48 超出1,需截断
|
||||
cci100.Get(i)/400, // 49 超出1,需截断
|
||||
cmf.Get(i),
|
||||
cmf50.Get(i),
|
||||
kama.Get(i)/closeVal-1,
|
||||
kama30.Get(i)/closeVal-1,
|
||||
willr.Get(i)/100,
|
||||
willr30.Get(i)/100,
|
||||
(willr30.Get(i)-willr30_5.Get(i))/100,
|
||||
willr50.Get(i)/100,
|
||||
srsiK.Get(i)/100, // 58 超出1,需截断
|
||||
(srsiK.Get(i)-srsiD.Get(i))/100,
|
||||
srsi2K.Get(i)/100,
|
||||
(srsi2K.Get(i)-srsi2D.Get(i))/100,
|
||||
mfi.Get(i)/100,
|
||||
mfi30.Get(i)/100,
|
||||
rmi.Get(i)/100,
|
||||
rmi30.Get(i)/100,
|
||||
closeVal/hh14Val,
|
||||
hh14Val/hh50.Get(i),
|
||||
ll14Val/closeVal,
|
||||
ll50.Get(i)/ll14Val,
|
||||
(hh14Val-ll14Val)/closeVal,
|
||||
td.Get(i)/30,
|
||||
)
|
||||
end := len(arrData)
|
||||
for j := offset + 4; j < end; j++ {
|
||||
arrData[j] = cutRange(arrData[j], -1, 1, 0)
|
||||
}
|
||||
offset = end
|
||||
}
|
||||
res := mat.NewDense(num, feaNum, arrData)
|
||||
// 对前四列hlcv进行归一化
|
||||
for i := 0; i < 4; i++ {
|
||||
col := res.ColView(i)
|
||||
minVal := mat.Min(col)
|
||||
maxVal := mat.Max(col)
|
||||
if minVal == maxVal {
|
||||
continue
|
||||
}
|
||||
for row := 0; row < num; row++ {
|
||||
oldValue := res.At(row, i)
|
||||
newValue := (oldValue - minVal) / (maxVal - minVal)
|
||||
res.Set(row, i, newValue)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/*
|
||||
GenFeaMats 生成时间、买、卖的热图
|
||||
*/
|
||||
func GenFeaMats(graph *ta.CGraph, num, depth int, restGap bool) ([3]*mat.Dense, float64) {
|
||||
times := mat.NewDense(num, depth, nil)
|
||||
buy := mat.NewDense(num, depth, nil)
|
||||
sell := mat.NewDense(num, depth, nil)
|
||||
pens := make([]*ta.CPen, 0, num)
|
||||
bar := graph.Bar(graph.ParseTo)
|
||||
price := bar.Close
|
||||
pen := graph.Pens[len(graph.Pens)-1]
|
||||
pt := pen.End
|
||||
if graph.ParseTo > pt.BarId {
|
||||
if pen.Dirt*(price-pt.Price) > 0 {
|
||||
// 未合并数据和最后一笔方向相同
|
||||
pen = pen.Clone()
|
||||
pen.End = pt.Clone().Move(graph.ParseTo, price)
|
||||
pens = append(pens, pen)
|
||||
} else {
|
||||
end := graph.NewPoint(-pt.Dirt, price, graph.ParseTo)
|
||||
newPen := graph.NewPen(pt, end)
|
||||
pens = append(pens, newPen, pen)
|
||||
}
|
||||
} else {
|
||||
pens = append(pens, pen)
|
||||
}
|
||||
for len(pens) < num && pen.Prev != nil {
|
||||
pens = append(pens, pen.Prev)
|
||||
pen = pen.Prev
|
||||
}
|
||||
minIdx := len(graph.Bars) - (graph.BarNum - pens[len(pens)-1].Start.BarId) - 1
|
||||
maxEnd := len(graph.Bars) - (graph.BarNum - graph.ParseTo)
|
||||
bars := graph.Bars[minIdx:maxEnd]
|
||||
upper, lower, sumVol := price, price, float64(0)
|
||||
for _, b := range bars {
|
||||
lower = min(lower, b.Low)
|
||||
upper = max(upper, b.High)
|
||||
sumVol += b.Volume
|
||||
}
|
||||
barNum := len(bars)
|
||||
avgVol := sumVol / float64(barNum)
|
||||
sub := max(upper-price, price-lower)
|
||||
width := sub * 2 / float64(depth) // 每一格的价格宽度
|
||||
lower = price - sub
|
||||
tfMSecs := min(bars[1].Time-bars[0].Time, bars[barNum-1].Time-bars[barNum-2].Time)
|
||||
barId, col := graph.ParseTo, num
|
||||
prevMS := int64(0)
|
||||
for _, p := range pens {
|
||||
penId := p.Start.BarId
|
||||
if barId > penId {
|
||||
col -= 1
|
||||
if col < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
for barId > penId {
|
||||
bar = graph.Bar(barId)
|
||||
o, h, l, c := bar.Open-lower, bar.High-lower, bar.Low-lower, bar.Close-lower
|
||||
if restGap && prevMS > 0 && prevMS-bar.Time > tfMSecs*3/2 {
|
||||
// 休市期间添加空白时间记录
|
||||
lackNum := math.Round(float64(prevMS-bar.Time) / float64(tfMSecs))
|
||||
for i := 0; i < depth; i++ {
|
||||
times.Set(col, i, lackNum)
|
||||
}
|
||||
col -= 1
|
||||
if col < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
barId -= 1
|
||||
prevMS = bar.Time
|
||||
barUp, barDn := max(o, c), min(o, c)
|
||||
total := h - l + (h - barUp) + (barDn - l)
|
||||
if total > 0 {
|
||||
upRate, dnRate := (h-barUp)*2/total, (barDn-l)*2/total
|
||||
solidRate := (barUp - barDn) / total
|
||||
addToArr(times, col, barUp, h, upRate*0.5, width)
|
||||
addToArr(times, col, barDn, barUp, solidRate*0.5, width)
|
||||
addToArr(times, col, l, barDn, dnRate*0.5, width)
|
||||
volRate := math.Log(bar.Volume/avgVol + 1)
|
||||
buyRate, sellRate := 0.7, 0.3
|
||||
if o > c {
|
||||
buyRate, sellRate = 0.3, 0.7
|
||||
}
|
||||
addToArr(buy, col, barUp, h, volRate*upRate*0.5, width)
|
||||
addToArr(buy, col, barDn, barUp, volRate*solidRate*buyRate, width)
|
||||
addToArr(buy, col, l, barDn, volRate*dnRate*0.5, width)
|
||||
addToArr(sell, col, barUp, h, volRate*upRate*0.5, width)
|
||||
addToArr(sell, col, barDn, barUp, volRate*solidRate*sellRate, width)
|
||||
addToArr(sell, col, l, barDn, volRate*dnRate*0.5, width)
|
||||
}
|
||||
}
|
||||
}
|
||||
return [3]*mat.Dense{times, buy, sell}, sub / price
|
||||
}
|
||||
|
||||
/*
|
||||
GenPenMats2
|
||||
经测试,80个pen,平均需要1k根K线,最少700根,最多3k根
|
||||
*/
|
||||
func GenPenMats2(graph *ta.CGraph, num, depth int, restGap bool) ([2]*mat.Dense, float64) {
|
||||
mats, rate := GenFeaMats(graph, num, depth, restGap)
|
||||
times, buy, sell := mats[0], mats[1], mats[2]
|
||||
one := mat.NewDense(num, depth, nil)
|
||||
two := mat.NewDense(num, depth, nil)
|
||||
times.Apply(func(i, j int, v float64) float64 {
|
||||
return revPow(v, 2, 0, 1, 0)
|
||||
}, times)
|
||||
one.Apply(func(i, j int, v float64) float64 {
|
||||
mid := (buy.At(i, j) + sell.At(i, j)) / 2 * (1 - v)
|
||||
return revPow(mid, 1.5, 0, 1, 0)
|
||||
}, times)
|
||||
two.Apply(func(i, j int, v float64) float64 {
|
||||
return revPow(v-sell.At(i, j), 1.5, -1, 1, 0)
|
||||
}, buy)
|
||||
return [2]*mat.Dense{one, two}, rate
|
||||
}
|
||||
|
||||
func revPow(v, cutRate, minVal, maxVal, nanTo float64) float64 {
|
||||
if math.IsNaN(v) || math.IsInf(nanTo, 0) {
|
||||
if nanTo < 0 {
|
||||
return v
|
||||
}
|
||||
return nanTo
|
||||
}
|
||||
powered := math.Pow(math.Abs(v), 0.33) / cutRate
|
||||
if v >= 0 {
|
||||
return min(maxVal, powered)
|
||||
}
|
||||
return max(minVal, -powered)
|
||||
}
|
||||
|
||||
func cutRange(v, minVal, maxVal, nanTo float64) float64 {
|
||||
if math.IsNaN(v) || math.IsInf(nanTo, 0) {
|
||||
if nanTo < 0 {
|
||||
return v
|
||||
}
|
||||
return nanTo
|
||||
}
|
||||
if v >= 0 {
|
||||
return min(maxVal, v)
|
||||
}
|
||||
return max(minVal, -v)
|
||||
}
|
||||
|
||||
/*
|
||||
addToArr
|
||||
将总值按low~high切分为格子加到arr上
|
||||
*/
|
||||
func addToArr(m *mat.Dense, col int, low, high, total, width float64) {
|
||||
if high <= low || low < 0 {
|
||||
return
|
||||
}
|
||||
low /= width
|
||||
high /= width
|
||||
all := high - low
|
||||
idx, down := int(low), low
|
||||
_, dimCol := m.Dims()
|
||||
if high > float64(dimCol) {
|
||||
high = float64(dimCol)
|
||||
}
|
||||
for float64(idx) < high {
|
||||
upp := min(high, float64(idx+1))
|
||||
m.Set(col, idx, (upp-down)/all*total)
|
||||
down = float64(idx)
|
||||
idx += 1
|
||||
}
|
||||
}
|
||||
|
||||
func getMatColSum(m *mat.Dense, col int) float64 {
|
||||
if col < 0 {
|
||||
_, colNum := m.Dims()
|
||||
col += colNum
|
||||
}
|
||||
arr := m.ColView(col)
|
||||
num := arr.Len()
|
||||
// res := make([]float64, 0, num)
|
||||
res := float64(0)
|
||||
for i := 0; i < num; i++ {
|
||||
// res = append(res, arr.AtVec(i))
|
||||
res += arr.AtVec(i)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func getMatRowSum(m *mat.Dense, row int) float64 {
|
||||
if row < 0 {
|
||||
rowNum, _ := m.Dims()
|
||||
row += rowNum
|
||||
}
|
||||
res := float64(0)
|
||||
arr := m.RawRowView(row)
|
||||
for _, v := range arr {
|
||||
res += v
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type ColSta struct {
|
||||
Nan int // nan数量
|
||||
Min float64
|
||||
Max float64
|
||||
Mean float64
|
||||
}
|
||||
|
||||
func staToStr(sta *ColSta) string {
|
||||
return fmt.Sprintf("nan:%v, mean: %.5f, [%.5f, %.5f]", sta.Nan, sta.Mean, sta.Min, sta.Max)
|
||||
}
|
||||
|
||||
func sampleArr(arr []float64) *ColSta {
|
||||
nanNum := 0
|
||||
sum := float64(0)
|
||||
valid := 0
|
||||
minVal, maxVal := math.MaxFloat64, -math.MaxFloat64
|
||||
for _, v := range arr {
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
nanNum += 1
|
||||
continue
|
||||
}
|
||||
sum += v
|
||||
valid += 1
|
||||
if v < minVal {
|
||||
minVal = v
|
||||
}
|
||||
if v > maxVal {
|
||||
maxVal = v
|
||||
}
|
||||
}
|
||||
if valid > 0 {
|
||||
sum /= float64(valid)
|
||||
}
|
||||
return &ColSta{Nan: nanNum, Min: minVal, Max: maxVal, Mean: sum}
|
||||
}
|
||||
|
||||
type BatchMap struct {
|
||||
codes []string
|
||||
envs []*ta.BarEnv
|
||||
mux sync.Mutex
|
||||
curBarMS int64 // 当前bar的13位毫秒时间戳
|
||||
data map[string]*biz.NumArr
|
||||
maxSize int
|
||||
}
|
||||
|
||||
func NewBatchMap(size int) *BatchMap {
|
||||
return &BatchMap{
|
||||
data: make(map[string]*biz.NumArr),
|
||||
maxSize: size,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BatchMap) Add(rsp biz.FeaFeeder_SubFeaturesServer, e *ta.BarEnv, barMs int64, items map[string]*biz.NumArr) *errs.Error {
|
||||
b.mux.Lock()
|
||||
defer b.mux.Unlock()
|
||||
if barMs > b.curBarMS {
|
||||
oldLen := len(b.codes)
|
||||
if oldLen > 0 {
|
||||
b.maxSize = max(b.maxSize, oldLen)
|
||||
corrs, err_ := getEnvsCorr(b.envs, 100)
|
||||
if err_ != nil {
|
||||
return errs.New(errs.CodeRunTime, err_)
|
||||
}
|
||||
b.data["corr"] = &biz.NumArr{
|
||||
Data: corrs,
|
||||
Shape: []int32{int32(oldLen), 2},
|
||||
}
|
||||
err_ = rsp.Send(&biz.ArrMap{
|
||||
Codes: b.codes,
|
||||
Mats: b.data,
|
||||
})
|
||||
if err_ != nil {
|
||||
return errs.New(errs.CodeRunTime, err_)
|
||||
}
|
||||
b.codes = make([]string, 0, b.maxSize)
|
||||
b.data = make(map[string]*biz.NumArr)
|
||||
b.envs = make([]*ta.BarEnv, 0, b.maxSize)
|
||||
}
|
||||
b.curBarMS = barMs
|
||||
}
|
||||
b.codes = append(b.codes, e.Symbol)
|
||||
b.envs = append(b.envs, e)
|
||||
for name, arr := range items {
|
||||
old, ok := b.data[name]
|
||||
if !ok {
|
||||
arr.Shape = append([]int32{1}, arr.Shape...)
|
||||
b.data[name] = arr
|
||||
} else {
|
||||
old.Data = append(old.Data, arr.Data...)
|
||||
old.Shape = append([]int32{old.Shape[0] + 1}, old.Shape[1:]...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEnvsCorr(envs []*ta.BarEnv, hisNum int) ([]float64, error) {
|
||||
if len(envs) < 2 {
|
||||
res := make([]float64, 0, len(envs)*2)
|
||||
for range envs {
|
||||
res = append(res, 0, 0)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
_, avgs, err := utils.CalcEnvsCorr(envs, hisNum)
|
||||
res := make([]float64, 0, len(avgs)*2)
|
||||
avg := floats.Sum(avgs) / float64(len(avgs))
|
||||
for _, v := range avgs {
|
||||
res = append(res, avg, v)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
124
rpc_fea/fea_base.go
Normal file
124
rpc_fea/fea_base.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/orm"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/errs"
|
||||
"github.com/banbox/banta"
|
||||
)
|
||||
|
||||
type GenFeaTask struct {
|
||||
TfSml string
|
||||
TfBig string
|
||||
WarmSml int
|
||||
WarmBig int
|
||||
TfMSecs int64
|
||||
TfMSecsBig int64
|
||||
NextObsNum int
|
||||
OnBar func(e *banta.BarEnv, b *banexg.Kline, futs []*banexg.Kline) *errs.Error
|
||||
OnInfoBar func(e *banta.BarEnv, b *banexg.Kline) *errs.Error
|
||||
OnEnvEnd func(bar *banexg.PairTFKline, adj *orm.AdjInfo)
|
||||
}
|
||||
|
||||
type CodeState struct {
|
||||
env *banta.BarEnv
|
||||
envBig *banta.BarEnv
|
||||
}
|
||||
|
||||
func pubFeaBase(exsList []*orm.ExSymbol, req *biz.SubReq, task *GenFeaTask) error {
|
||||
var states = make(map[string]*CodeState)
|
||||
for _, exs := range exsList {
|
||||
states[exs.Symbol] = &CodeState{
|
||||
env: &banta.BarEnv{
|
||||
TimeFrame: task.TfSml,
|
||||
TFMSecs: task.TfMSecs,
|
||||
Exchange: exs.Exchange,
|
||||
MarketType: exs.Market,
|
||||
Symbol: exs.Symbol,
|
||||
MaxCache: 1000,
|
||||
},
|
||||
envBig: &banta.BarEnv{
|
||||
TimeFrame: task.TfBig,
|
||||
TFMSecs: task.TfMSecsBig,
|
||||
Exchange: exs.Exchange,
|
||||
MarketType: exs.Market,
|
||||
Symbol: exs.Symbol,
|
||||
MaxCache: 500,
|
||||
},
|
||||
}
|
||||
}
|
||||
var outErr *errs.Error
|
||||
verCh := make(chan int, 5)
|
||||
stopWithErr := func(err error) {
|
||||
outErr = errs.New(errs.CodeRunTime, err)
|
||||
select {
|
||||
case verCh <- -1:
|
||||
default:
|
||||
}
|
||||
}
|
||||
onData := func(evt *orm.DataSeries, nexts []*orm.DataSeries) {
|
||||
bar, err_ := orm.AsKline(evt)
|
||||
if err_ != nil {
|
||||
stopWithErr(err_)
|
||||
return
|
||||
}
|
||||
state := states[bar.Symbol]
|
||||
var err *errs.Error
|
||||
if bar.TimeFrame == task.TfSml {
|
||||
futs := make([]*banexg.Kline, 0, len(nexts))
|
||||
for _, n := range nexts {
|
||||
nextBar, convErr := orm.AsKline(n)
|
||||
if convErr != nil {
|
||||
stopWithErr(convErr)
|
||||
return
|
||||
}
|
||||
futs = append(futs, &nextBar.Kline)
|
||||
}
|
||||
state.env.OnBar(bar.Time, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume,
|
||||
bar.Quote, bar.BuyVolume, bar.TradeNum)
|
||||
err = task.OnBar(state.env, &bar.Kline, futs)
|
||||
} else {
|
||||
state.envBig.OnBar(bar.Time, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume,
|
||||
bar.Quote, bar.BuyVolume, bar.TradeNum)
|
||||
err = task.OnInfoBar(state.envBig, &bar.Kline)
|
||||
}
|
||||
if err != nil {
|
||||
outErr = err
|
||||
verCh <- -1
|
||||
}
|
||||
}
|
||||
err := biz.RunHistSeries(&biz.RunHistSeriesArgs{
|
||||
ExsList: exsList,
|
||||
Start: req.Start,
|
||||
End: req.End,
|
||||
ViewNextNum: task.NextObsNum,
|
||||
TfWarms: map[string]int{
|
||||
task.TfSml: task.WarmSml,
|
||||
task.TfBig: task.WarmBig,
|
||||
},
|
||||
OnEnvEnd: func(evt *orm.DataSeries) {
|
||||
if evt == nil {
|
||||
task.OnEnvEnd(nil, nil)
|
||||
return
|
||||
}
|
||||
bar, err_ := orm.AsKline(evt)
|
||||
if err_ != nil {
|
||||
stopWithErr(err_)
|
||||
return
|
||||
}
|
||||
if bar.PairTFKline != nil {
|
||||
state := states[bar.Symbol]
|
||||
state.env.Reset()
|
||||
state.envBig.Reset()
|
||||
}
|
||||
task.OnEnvEnd(bar.PairTFKline, bar.Adj)
|
||||
},
|
||||
VerCh: verCh,
|
||||
OnData: onData,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return outErr
|
||||
}
|
||||
107
rpc_fea/fea_times.go
Normal file
107
rpc_fea/fea_times.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/btime"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/errs"
|
||||
"github.com/banbox/banexg/log"
|
||||
utils2 "github.com/banbox/banexg/utils"
|
||||
"github.com/banbox/banta"
|
||||
"go.uber.org/zap"
|
||||
"gonum.org/v1/gonum/mat"
|
||||
)
|
||||
|
||||
func pubFeaTimes(exsList []*orm.ExSymbol, req *biz.SubReq, rsp biz.FeaFeeder_SubFeaturesServer) error {
|
||||
var sample = int(req.Sample)
|
||||
if sample <= 0 {
|
||||
sample = 10
|
||||
}
|
||||
startDt := btime.ToDateStr(req.Start, core.DefaultDateFmt)
|
||||
log.Info("start fea_times", zap.Int("pair", len(exsList)), zap.String("start", startDt),
|
||||
zap.Int("sample", sample))
|
||||
tfSml, tfBig := "5m", "1h"
|
||||
tfMSecs := int64(utils2.TFToSecs(tfSml) * 1000)
|
||||
tfMSecsBig := int64(utils2.TFToSecs(tfBig) * 1000)
|
||||
seqNum := 150 // 特征的历史长度
|
||||
|
||||
var feasBig *mat.Dense
|
||||
var avgCostBigs []float64 //大周期的成本价
|
||||
|
||||
task := &GenFeaTask{
|
||||
TfSml: tfSml,
|
||||
TfBig: tfBig,
|
||||
WarmSml: 500,
|
||||
WarmBig: 500,
|
||||
TfMSecs: tfMSecs,
|
||||
TfMSecsBig: tfMSecsBig,
|
||||
NextObsNum: 30, // 预测后n个bar的行情
|
||||
}
|
||||
|
||||
batch := NewBatchMap(len(exsList))
|
||||
//taskId := orm.GetTaskID("")
|
||||
//outDir := fmt.Sprintf("%s/backtest/task_%d", config.GetDataDir(), taskId)
|
||||
//var outputs []string
|
||||
|
||||
task.OnBar = func(e *banta.BarEnv, b *banexg.Kline, futs []*banexg.Kline) *errs.Error {
|
||||
feas := onAiFeatures(e, seqNum)
|
||||
if e.BarNum%sample > 0 || feasBig == nil {
|
||||
return nil
|
||||
}
|
||||
atr30 := banta.ATR(e.High, e.Low, e.Close, 30).Get(0)
|
||||
minChgVal := atr30 * 5
|
||||
futProbs := genFutProbsV1(futs, minChgVal, 0.4)
|
||||
|
||||
//if !core.IsWarmUp {
|
||||
// curDate := btime.ToDateStr(e.TimeStart, core.DefaultDateFmt)
|
||||
// curCloseVal := e.Close.Get(0)
|
||||
// n30Close := futs[len(futs)-1].Close
|
||||
// row := fmt.Sprintf("%s, %.5f, %.5f, %.4f, %.4f, %.4f, %.7f", curDate, curCloseVal, n30Close,
|
||||
// futProbs[0], futProbs[1], futProbs[2], atr30)
|
||||
// outputs = append(outputs, row)
|
||||
//}
|
||||
|
||||
avgCosts := avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
info := make([]float64, 0, 12)
|
||||
info = append(info, avgCosts...)
|
||||
info = append(info, avgCostBigs...)
|
||||
return batch.Add(rsp, e, b.Time, map[string]*biz.NumArr{
|
||||
"feas": matToNumArr(feas),
|
||||
"feas2": matToNumArr(feasBig),
|
||||
"bar": {
|
||||
Data: []float64{float64(b.Time / 1000), b.Open, b.High, b.Low, b.Close, b.Volume},
|
||||
Shape: []int32{6},
|
||||
},
|
||||
"y": {
|
||||
Data: []float64{futProbs[0], futProbs[1], futProbs[2]},
|
||||
Shape: []int32{3},
|
||||
},
|
||||
"info": {Data: info, Shape: []int32{int32(len(info))}},
|
||||
})
|
||||
}
|
||||
task.OnInfoBar = func(e *banta.BarEnv, b *banexg.Kline) *errs.Error {
|
||||
feasBig = onAiFeatures(e, seqNum)
|
||||
avgCostBigs = avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
return nil
|
||||
}
|
||||
task.OnEnvEnd = func(bar *banexg.PairTFKline, adj *orm.AdjInfo) {
|
||||
//name := "fea_times"
|
||||
//outPath := fmt.Sprintf("%s/%s.log", outDir, name)
|
||||
//file, err := os.Create(outPath)
|
||||
//if err != nil {
|
||||
// log.Error("create strategy output file fail", zap.String("name", name), zap.Error(err))
|
||||
// return
|
||||
//}
|
||||
//_, err = file.WriteString(strings.Join(outputs, "\n"))
|
||||
//if err != nil {
|
||||
// log.Error("write strategy output fail", zap.String("name", name), zap.Error(err))
|
||||
//}
|
||||
//err = file.Close()
|
||||
//if err != nil {
|
||||
// log.Error("close strategy output fail", zap.String("name", name), zap.Error(err))
|
||||
//}
|
||||
}
|
||||
return pubFeaBase(exsList, req, task)
|
||||
}
|
||||
383
rpc_fea/fea_trade1.go
Normal file
383
rpc_fea/fea_trade1.go
Normal file
@@ -0,0 +1,383 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm/ormo"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banexg"
|
||||
"github.com/banbox/banexg/log"
|
||||
ta "github.com/banbox/banta"
|
||||
"go.uber.org/zap"
|
||||
"gonum.org/v1/gonum/mat"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"math"
|
||||
)
|
||||
|
||||
type AIMore struct {
|
||||
cg *ta.CGraph
|
||||
cgBig *ta.CGraph
|
||||
matBig [3]*mat.Dense // 大周期行情
|
||||
feasBig *mat.Dense // 大周期特征
|
||||
mat [3]*mat.Dense // 小周期行情
|
||||
feas *mat.Dense // 小周期特征
|
||||
rateBig float64 // 大周期边界倍率
|
||||
rate float64 // 边界倍率
|
||||
costBigs []float64 // 大周期成本价
|
||||
costs []float64 // 小周期成本价
|
||||
info []float64
|
||||
hisProbs []float64 // 历史行情概率
|
||||
diff []float64 // 历史行情概率与真实的差
|
||||
pftDoneLong float64 // 已平仓的做多利润
|
||||
pftDoneShort float64 // 已平仓做空利润
|
||||
pftUPolLong float64 // 未平仓做多利润
|
||||
pftUPolShort float64 // 未平仓做空利润
|
||||
atr float64
|
||||
trues []float64 // 记录最近probs是否相同
|
||||
age int // 已持仓bar数
|
||||
}
|
||||
|
||||
func AITrade(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
var seqNum = 150 // 特征序列长度
|
||||
var penNum = 120 // 笔矩阵长度
|
||||
var depth = 100 // 笔矩阵深度
|
||||
var hisProbLen = 100 // 历史行情置信度长度
|
||||
var maxOdNum = 1.0 // 单币种单方向最多开1单
|
||||
var nextObsNum = 30 // 预测后n个bar的收益
|
||||
var diffLen = hisProbLen - nextObsNum
|
||||
var indsDepth = 30*2 + 14
|
||||
const maxMsgSize = 100 * 1024 * 1024
|
||||
const maxHoldAge = 300 // 持仓age超过此配置,强制平仓
|
||||
addr := "192.168.7.253:8080"
|
||||
creds := grpc.WithTransportCredentials(insecure.NewCredentials())
|
||||
conn, err_ := grpc.NewClient(addr, creds, grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallSendMsgSize(maxMsgSize),
|
||||
grpc.MaxCallRecvMsgSize(maxMsgSize),
|
||||
))
|
||||
if err_ != nil {
|
||||
panic(err_)
|
||||
}
|
||||
client := biz.NewAInferClient(conn)
|
||||
return &strat.TradeStrat{
|
||||
BatchInOut: true,
|
||||
//DrawDownExit: true,
|
||||
WarmupNum: 3000,
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &AIMore{
|
||||
cg: &ta.CGraph{ParseLevel: ta.ParseLevelPen},
|
||||
cgBig: &ta.CGraph{ParseLevel: ta.ParseLevelPen},
|
||||
}
|
||||
},
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
return []*strat.PairSub{
|
||||
{"_cur_", "1h", 3000},
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
m, _ := s.More.(*AIMore)
|
||||
e := s.Env
|
||||
//if !core.IsWarmUp {
|
||||
// log.Info("OnBar", zap.Int64("time", e.TimeStop))
|
||||
// defer log.Info("OnBarEnd", zap.Int64("time", e.TimeStop))
|
||||
//}
|
||||
m.cg.AddBar(e)
|
||||
m.cg.Parse()
|
||||
m.feas = onAiFeatures(e, seqNum)
|
||||
if len(m.cg.Pens) < 10 {
|
||||
return
|
||||
}
|
||||
m.mat, m.rate = GenFeaMats(m.cg, penNum, depth, false)
|
||||
m.costs = avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
o, h, l, c, v := e.Open.Get(0), e.High.Get(0), e.Low.Get(0), e.Close.Get(0), e.Volume.Get(0)
|
||||
info := []float64{float64(e.TimeStart / 1000), o, h, l, c, v,
|
||||
math.Log1p(m.rate), math.Log1p(m.rateBig)}
|
||||
info = append(info, m.costs...)
|
||||
info = append(info, m.costBigs...)
|
||||
info = append(info, 0, 0, 0)
|
||||
m.info = info
|
||||
m.atr = ta.ATR(e.High, e.Low, e.Close, 30).Get(0)
|
||||
m.pftUPolShort = 0
|
||||
m.pftUPolLong = 0
|
||||
if len(s.LongOrders) == 0 && len(s.ShortOrders) == 0 {
|
||||
m.pftDoneLong = 0
|
||||
m.pftDoneShort = 0
|
||||
} else {
|
||||
for _, od := range s.LongOrders {
|
||||
if od.Status >= ormo.InOutStatusFullEnter {
|
||||
m.pftUPolLong += od.ProfitRate
|
||||
}
|
||||
}
|
||||
for _, od := range s.ShortOrders {
|
||||
if od.Status >= ormo.InOutStatusFullEnter {
|
||||
m.pftUPolShort += od.ProfitRate
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
|
||||
//if !core.IsWarmUp {
|
||||
// log.Info("OnInfoBar", zap.Int64("time", e.TimeStop))
|
||||
// defer log.Info("OnInfoBarEnd", zap.Int64("time", e.TimeStop))
|
||||
//}
|
||||
m, _ := s.More.(*AIMore)
|
||||
m.cgBig.AddBar(e)
|
||||
m.cgBig.Parse()
|
||||
m.feasBig = onAiFeatures(e, seqNum)
|
||||
m.costBigs = avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
if len(m.cgBig.Pens) >= penNum {
|
||||
m.matBig, m.rateBig = GenFeaMats(m.cgBig, penNum, depth, false)
|
||||
}
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
var valids []*strat.StratJob
|
||||
var time1, buy1, sell1, feas1, time2, buy2, sell2, feas2, info []float64
|
||||
var graphLen, graphDepth, feasLen, feasDepth, infoDepth int
|
||||
for _, j := range jobs {
|
||||
m, _ := j.More.(*AIMore)
|
||||
if m.matBig[0] == nil || m.mat[0] == nil || len(m.info) != 23 {
|
||||
continue
|
||||
}
|
||||
if len(valids) == 0 {
|
||||
graphLen, graphDepth = m.mat[0].Dims()
|
||||
feasLen, feasDepth = m.feas.Dims()
|
||||
infoDepth = len(m.info)
|
||||
}
|
||||
valids = append(valids, j)
|
||||
time1 = append(time1, m.mat[0].RawMatrix().Data...)
|
||||
buy1 = append(buy1, m.mat[1].RawMatrix().Data...)
|
||||
sell1 = append(sell1, m.mat[2].RawMatrix().Data...)
|
||||
feas1 = append(feas1, m.feas.RawMatrix().Data...)
|
||||
time2 = append(time2, m.matBig[0].RawMatrix().Data...)
|
||||
buy2 = append(buy2, m.matBig[1].RawMatrix().Data...)
|
||||
sell2 = append(sell2, m.matBig[2].RawMatrix().Data...)
|
||||
feas2 = append(feas2, m.feasBig.RawMatrix().Data...)
|
||||
info = append(info, m.info...)
|
||||
}
|
||||
//if !core.IsWarmUp {
|
||||
// log.Info("OnBatchJobs", zap.Int64("time", timeStop))
|
||||
// defer log.Info("OnBatchJobs", zap.Int64("time", timeStop))
|
||||
//}
|
||||
if len(valids) == 0 {
|
||||
return
|
||||
}
|
||||
bSize := len(valids)
|
||||
klineShape := []int32{int32(bSize), int32(graphLen), int32(graphDepth)}
|
||||
feaShape := []int32{int32(bSize), int32(feasLen), int32(feasDepth)}
|
||||
ctx := context.Background()
|
||||
|
||||
trend, err_ := client.Trend(ctx, &biz.ArrMap{
|
||||
Mats: map[string]*biz.NumArr{
|
||||
"time": {Data: time1, Shape: klineShape},
|
||||
"buy": {Data: buy1, Shape: klineShape},
|
||||
"sell": {Data: sell1, Shape: klineShape},
|
||||
"feas": {Data: feas1, Shape: feaShape},
|
||||
"time2": {Data: time2, Shape: klineShape},
|
||||
"buy2": {Data: buy2, Shape: klineShape},
|
||||
"sell2": {Data: sell2, Shape: klineShape},
|
||||
"feas2": {Data: feas2, Shape: feaShape},
|
||||
"info": {Data: info, Shape: []int32{int32(bSize), int32(infoDepth)}},
|
||||
},
|
||||
})
|
||||
if err_ != nil {
|
||||
log.Panic("call ai trend fail", zap.Int("bSize", bSize), zap.Error(err_))
|
||||
}
|
||||
probs := trend.Mats["probs"].Data
|
||||
if len(probs) != bSize*3 {
|
||||
panic(fmt.Sprintf("probs length error, expect: %v, got: %v", len(probs), bSize*3))
|
||||
}
|
||||
infoMat := trend.Mats["info"]
|
||||
rawInfo := infoMat.Data
|
||||
rawInfoDepth := int(infoMat.Shape[1])
|
||||
hisMin := hisProbLen * 3
|
||||
var his, diff, inds []float64
|
||||
info = nil
|
||||
var items []*strat.StratJob
|
||||
for i, j := range valids {
|
||||
m, _ := j.More.(*AIMore)
|
||||
curProbs := probs[i*3 : (i+1)*3]
|
||||
m.hisProbs = append(m.hisProbs, curProbs...)
|
||||
if len(m.hisProbs) > nextObsNum*3 {
|
||||
start := len(m.hisProbs) - (nextObsNum+1)*3
|
||||
p30Preds := m.hisProbs[start : start+3]
|
||||
p30Trues := genCurFutProbs(j.Env)
|
||||
m.diff = append(m.diff, math.Abs(p30Trues[0]-p30Preds[0]),
|
||||
math.Abs(p30Trues[1]-p30Preds[1]),
|
||||
math.Abs(p30Trues[2]-p30Preds[2]))
|
||||
m.logTrue([3]float64{p30Trues[0], p30Trues[1], p30Trues[2]}, p30Preds)
|
||||
}
|
||||
if len(m.hisProbs) >= hisMin {
|
||||
m.hisProbs = m.hisProbs[len(m.hisProbs)-hisMin:]
|
||||
items = append(items, j)
|
||||
his = append(his, m.hisProbs...)
|
||||
m.diff = m.diff[len(m.diff)-diffLen*3:]
|
||||
diff = append(diff, m.diff...)
|
||||
m.trues = m.trues[len(m.trues)-diffLen:]
|
||||
inds = append(inds, m.feas.RawRowView(feasLen-1)...)
|
||||
inds = append(inds, m.feasBig.RawRowView(feasLen-1)...)
|
||||
inds = append(inds, rawInfo[i*rawInfoDepth:(i+1)*rawInfoDepth]...)
|
||||
info = append(info, m.pftDoneLong/maxOdNum, m.pftDoneShort/maxOdNum,
|
||||
m.pftUPolLong/maxOdNum, m.pftUPolShort/maxOdNum,
|
||||
float64(len(j.LongOrders))/maxOdNum, float64(len(j.ShortOrders))/maxOdNum,
|
||||
avgArr(m.trues),
|
||||
)
|
||||
if m.age > 0 && len(j.LongOrders) == 0 && len(j.ShortOrders) == 0 {
|
||||
m.age = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
bSize = len(items)
|
||||
if bSize == 0 {
|
||||
return
|
||||
}
|
||||
trade, err_ := client.Trade(ctx, &biz.ArrMap{
|
||||
Mats: map[string]*biz.NumArr{
|
||||
"his": {Data: his, Shape: []int32{int32(bSize), int32(hisProbLen), 3}},
|
||||
"diff": {Data: diff, Shape: []int32{int32(bSize), int32(diffLen), 3}},
|
||||
"inds": {Data: inds, Shape: []int32{int32(bSize), int32(indsDepth)}},
|
||||
"info": {Data: info, Shape: []int32{int32(bSize), 7}},
|
||||
},
|
||||
})
|
||||
if err_ != nil {
|
||||
panic(err_)
|
||||
}
|
||||
actions := trade.Mats["actions"].Data
|
||||
for i, j := range items {
|
||||
m, _ := j.More.(*AIMore)
|
||||
act := int(math.Round(actions[i]))
|
||||
truncated := false
|
||||
if len(j.LongOrders) > 0 || len(j.ShortOrders) > 0 {
|
||||
m.age += 1
|
||||
if m.age >= maxHoldAge {
|
||||
truncated = true
|
||||
}
|
||||
}
|
||||
if (act == 3 || truncated) && len(j.LongOrders) > 0 {
|
||||
_ = j.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
if truncated || act == 4 && len(j.ShortOrders) > 0 {
|
||||
_ = j.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
} else if act == 1 && len(j.LongOrders) < int(maxOdNum) {
|
||||
//m, _ := j.More.(*AIMore)
|
||||
_ = j.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
//StopLossVal: m.atr * 10,
|
||||
})
|
||||
} else if act == 2 && len(j.ShortOrders) < int(maxOdNum) {
|
||||
//m, _ := j.More.(*AIMore)
|
||||
_ = j.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
Short: true,
|
||||
//StopLossVal: m.atr * 10,
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
//OnCheckExit: func(s *strategy.StratJob, od *ormo.InOutOrder) *strategy.ExitReq {
|
||||
// slVal := od.GetInfoFloat64(orm.OdInfoStopLossVal)
|
||||
// holdNum := int((s.Env.TimeStop - od.EnterAt) / s.Env.TFMSecs)
|
||||
// rate := math.Abs(s.Env.Close.Get(0)-od.InitPrice) / slVal
|
||||
// if holdNum > nextObsNum && (od.ProfitRate < 0 || rate < 0.3) {
|
||||
// // 止损:持仓30个bar && (亏损状态 or 盈利不足最小预期30%)
|
||||
// return &strategy.ExitReq{
|
||||
// Tag: "exit_bad",
|
||||
// OrderID: od.ID,
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
//},
|
||||
GetDrawDownExitRate: func(s *strat.StratJob, od *ormo.InOutOrder, maxChg float64) float64 {
|
||||
sl := od.GetStopLoss()
|
||||
if sl == nil || sl.Range == 0 {
|
||||
return 0
|
||||
}
|
||||
bestRate := maxChg * od.InitPrice / sl.Range
|
||||
if bestRate > 1.2 {
|
||||
// 最大盈利是止损区间2倍,以30%回撤止盈
|
||||
return 0.3
|
||||
}
|
||||
return 0
|
||||
},
|
||||
OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) {
|
||||
m, _ := s.More.(*AIMore)
|
||||
if chgType == strat.OdChgExitFill {
|
||||
if od.Short {
|
||||
m.pftDoneShort += od.ProfitRate
|
||||
} else {
|
||||
m.pftDoneLong += od.ProfitRate
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func compareProb(trues [3]float64, preds []float64) float64 {
|
||||
var mx, maxv = 0, trues[0]
|
||||
for i := 1; i < 3; i++ {
|
||||
if trues[i] > maxv {
|
||||
maxv = trues[i]
|
||||
mx = i
|
||||
}
|
||||
}
|
||||
var argMaxA = mx
|
||||
mx, maxv = 0, preds[0]
|
||||
for i := 1; i < 3; i++ {
|
||||
if preds[i] > maxv {
|
||||
maxv = preds[i]
|
||||
mx = i
|
||||
}
|
||||
}
|
||||
if argMaxA == mx {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AIMore) logTrue(trues [3]float64, preds []float64) {
|
||||
val := compareProb(trues, preds)
|
||||
m.trues = append(m.trues, val)
|
||||
}
|
||||
|
||||
func genCurFutProbs(e *ta.BarEnv) [3]float64 {
|
||||
var nextObsNum = 30 // 预测后n个bar的收益
|
||||
var bars = make([]*banexg.Kline, 0, nextObsNum)
|
||||
// opens等都是逆序的,第0个是最新的
|
||||
opens := e.Open.Range(0, nextObsNum)
|
||||
highs := e.High.Range(0, nextObsNum)
|
||||
lows := e.Low.Range(0, nextObsNum)
|
||||
closes := e.Close.Range(0, nextObsNum)
|
||||
vols := e.Volume.Range(0, nextObsNum)
|
||||
for i := len(opens) - 1; i >= 0; i-- {
|
||||
// 从后往前,即按时间顺序
|
||||
bars = append(bars, &banexg.Kline{
|
||||
Open: opens[i],
|
||||
High: highs[i],
|
||||
Low: lows[i],
|
||||
Close: closes[i],
|
||||
Volume: vols[i],
|
||||
})
|
||||
}
|
||||
minChgVal := ta.ATR(e.High, e.Low, e.Close, 30).Get(nextObsNum) * 5
|
||||
return genFutProbsV1(bars, minChgVal, 0.4)
|
||||
}
|
||||
|
||||
func avgArr(data []float64) float64 {
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
var sum = float64(0)
|
||||
for _, v := range data {
|
||||
sum += v
|
||||
}
|
||||
return sum / float64(len(data))
|
||||
}
|
||||
324
rpc_fea/fea_trade2.go
Normal file
324
rpc_fea/fea_trade2.go
Normal file
@@ -0,0 +1,324 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/config"
|
||||
"github.com/banbox/banbot/core"
|
||||
"github.com/banbox/banbot/orm/ormo"
|
||||
"github.com/banbox/banbot/strat"
|
||||
"github.com/banbox/banexg/log"
|
||||
ta "github.com/banbox/banta"
|
||||
"go.uber.org/zap"
|
||||
"gonum.org/v1/gonum/mat"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"math"
|
||||
)
|
||||
|
||||
type AIMore2 struct {
|
||||
cg *ta.CGraph
|
||||
feasBig *mat.Dense // 大周期特征
|
||||
mat [2]*mat.Dense // 小周期行情
|
||||
feas *mat.Dense // 小周期特征
|
||||
costBigs []float64 // 大周期成本价
|
||||
costs []float64 // 小周期成本价
|
||||
info []float64
|
||||
hisProbs []float64 // 历史行情概率
|
||||
diff []float64 // 历史行情概率与真实的差
|
||||
pftDoneLong float64 // 已平仓的做多利润
|
||||
pftDoneShort float64 // 已平仓做空利润
|
||||
pftUPolLong float64 // 未平仓做多利润
|
||||
pftUPolShort float64 // 未平仓做空利润
|
||||
atr float64
|
||||
trues []float64 // 记录最近probs是否相同
|
||||
age int // 已持仓bar数
|
||||
}
|
||||
|
||||
func AITrade2(pol *config.RunPolicyConfig) *strat.TradeStrat {
|
||||
var seqNum = 150 // 特征序列长度
|
||||
var penNum = 120 // 笔矩阵长度
|
||||
var depth = 100 // 笔矩阵深度
|
||||
var hisProbLen = 100 // 历史行情置信度长度
|
||||
var maxOdNum = 1.0 // 单币种单方向最多开1单
|
||||
var nextObsNum = 30 // 预测后n个bar的收益
|
||||
var diffLen = hisProbLen - nextObsNum
|
||||
var indsDepth = 72 + 12
|
||||
const maxMsgSize = 100 * 1024 * 1024
|
||||
const maxHoldAge = 100 // 持仓age超过此配置,强制平仓
|
||||
addr := "192.168.7.253:8080"
|
||||
creds := grpc.WithTransportCredentials(insecure.NewCredentials())
|
||||
conn, err_ := grpc.NewClient(addr, creds, grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallSendMsgSize(maxMsgSize),
|
||||
grpc.MaxCallRecvMsgSize(maxMsgSize),
|
||||
))
|
||||
if err_ != nil {
|
||||
panic(err_)
|
||||
}
|
||||
client := biz.NewAInferClient(conn)
|
||||
return &strat.TradeStrat{
|
||||
BatchInOut: true,
|
||||
DrawDownExit: true,
|
||||
WarmupNum: 3000,
|
||||
OnStartUp: func(s *strat.StratJob) {
|
||||
s.More = &AIMore2{
|
||||
cg: &ta.CGraph{ParseLevel: ta.ParseLevelPen, MaxPen: penNum * 3 / 2},
|
||||
}
|
||||
},
|
||||
OnPairInfos: func(s *strat.StratJob) []*strat.PairSub {
|
||||
return []*strat.PairSub{
|
||||
{"_cur_", "1h", 300},
|
||||
}
|
||||
},
|
||||
OnBar: func(s *strat.StratJob) {
|
||||
m, _ := s.More.(*AIMore2)
|
||||
e := s.Env
|
||||
m.cg.AddBar(e)
|
||||
m.cg.Parse()
|
||||
m.feas = onAiFeatures2(e, 30)
|
||||
if len(m.cg.Pens) < 10 {
|
||||
return
|
||||
}
|
||||
m.mat, _ = GenPenMats2(m.cg, penNum, depth, false)
|
||||
m.costs = avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
o, h, l, c, v := e.Open.Get(0), e.High.Get(0), e.Low.Get(0), e.Close.Get(0), e.Volume.Get(0)
|
||||
info := make([]float64, 0, 20)
|
||||
info = append(info, float64(e.TimeStart/1000), o, h, l, c, v)
|
||||
info = append(info, m.costs...)
|
||||
info = append(info, m.costBigs...)
|
||||
m.info = info
|
||||
m.atr = ta.ATR(e.High, e.Low, e.Close, 30).Get(0)
|
||||
m.pftUPolShort = 0
|
||||
m.pftUPolLong = 0
|
||||
if len(s.LongOrders) == 0 && len(s.ShortOrders) == 0 {
|
||||
m.pftDoneLong = 0
|
||||
m.pftDoneShort = 0
|
||||
} else {
|
||||
for _, od := range s.LongOrders {
|
||||
if od.Status >= ormo.InOutStatusFullEnter {
|
||||
m.pftUPolLong += od.ProfitRate
|
||||
}
|
||||
}
|
||||
for _, od := range s.ShortOrders {
|
||||
if od.Status >= ormo.InOutStatusFullEnter {
|
||||
m.pftUPolShort += od.ProfitRate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if !s.IsWarmUp {
|
||||
// curDate := btime.ToDateStr(e.TimeStart, core.DefaultDateFmt)
|
||||
// curCloseVal := e.Close.Get(0)
|
||||
// a := staToStr(sampleArr(m.mat[0].RawMatrix().Data))
|
||||
// b := staToStr(sampleArr(m.mat[1].RawMatrix().Data))
|
||||
// cc := staToStr(sampleArr(m.feas.RawMatrix().Data))
|
||||
// d := staToStr(sampleArr(m.feasBig.RawMatrix().Data))
|
||||
// row := fmt.Sprintf("%s, %.5f, %s, %s, %s, %s", curDate, curCloseVal, a, b, cc, d)
|
||||
// s.Strat.Outputs = append(s.Strat.Outputs, row)
|
||||
//}
|
||||
},
|
||||
OnInfoBar: func(s *strat.StratJob, e *ta.BarEnv, pair, tf string) {
|
||||
m, _ := s.More.(*AIMore2)
|
||||
m.feasBig = onAiFeatures2(e, seqNum)
|
||||
m.costBigs = avgCostN(e, 10, 20, 40, 80, 160, 320)
|
||||
},
|
||||
OnBatchJobs: func(jobs []*strat.StratJob) {
|
||||
var valids []*strat.StratJob
|
||||
var one, two, feas1, feas2, bars, info []float64
|
||||
var graphLen, graphDepth, feasLen, feasLen2, feasDepth, infoDepth int
|
||||
for _, j := range jobs {
|
||||
m, _ := j.More.(*AIMore2)
|
||||
if m.mat[0] == nil || len(m.info) != 18 {
|
||||
continue
|
||||
}
|
||||
if len(valids) == 0 {
|
||||
graphLen, graphDepth = m.mat[0].Dims()
|
||||
feasLen, feasDepth = m.feas.Dims()
|
||||
feasLen2, _ = m.feasBig.Dims()
|
||||
infoDepth = len(m.info) - 6
|
||||
}
|
||||
valids = append(valids, j)
|
||||
one = append(one, m.mat[0].RawMatrix().Data...)
|
||||
two = append(two, m.mat[1].RawMatrix().Data...)
|
||||
feas1 = append(feas1, m.feas.RawMatrix().Data...)
|
||||
feas2 = append(feas2, m.feasBig.RawMatrix().Data...)
|
||||
bars = append(bars, m.info[:6]...)
|
||||
info = append(info, m.info[6:]...)
|
||||
}
|
||||
if len(valids) == 0 {
|
||||
return
|
||||
}
|
||||
bSize := len(valids)
|
||||
klineShape := []int32{int32(bSize), int32(graphLen), int32(graphDepth)}
|
||||
feaShape1 := []int32{int32(bSize), int32(feasLen), int32(feasDepth)}
|
||||
feaShape2 := []int32{int32(bSize), int32(feasLen2), int32(feasDepth)}
|
||||
ctx := context.Background()
|
||||
|
||||
trend, err_ := client.Trend(ctx, &biz.ArrMap{
|
||||
Mats: map[string]*biz.NumArr{
|
||||
"one": {Data: one, Shape: klineShape},
|
||||
"two": {Data: two, Shape: klineShape},
|
||||
"feas": {Data: feas1, Shape: feaShape1},
|
||||
"feas2": {Data: feas2, Shape: feaShape2},
|
||||
"bar": {Data: bars, Shape: []int32{int32(bSize), 6}},
|
||||
"info": {Data: info, Shape: []int32{int32(bSize), int32(infoDepth)}},
|
||||
},
|
||||
})
|
||||
if err_ != nil {
|
||||
log.Panic("call ai trend fail", zap.Int("bSize", bSize), zap.Error(err_))
|
||||
}
|
||||
probs := trend.Mats["probs"].Data
|
||||
outDim := 12
|
||||
if len(probs) != bSize*outDim {
|
||||
panic(fmt.Sprintf("probs length error, expect: %v, got: %v", bSize*outDim, len(probs)))
|
||||
}
|
||||
infoMat := trend.Mats["info"]
|
||||
rawInfo := infoMat.Data
|
||||
rawInfoDepth := int(infoMat.Shape[1])
|
||||
hisMin := hisProbLen * outDim
|
||||
var his, diff, inds []float64
|
||||
info = nil
|
||||
var items []*strat.StratJob
|
||||
for i, j := range valids {
|
||||
m, _ := j.More.(*AIMore2)
|
||||
curProbs := probs[i*outDim : (i+1)*outDim]
|
||||
m.hisProbs = append(m.hisProbs, curProbs...)
|
||||
if len(m.hisProbs) > nextObsNum*outDim {
|
||||
start := len(m.hisProbs) - (nextObsNum+1)*outDim
|
||||
p30Preds := m.hisProbs[start : start+outDim]
|
||||
p30Trues := genCurFutProbs(j.Env)
|
||||
m.diff = append(m.diff, math.Abs(p30Trues[0]-p30Preds[0]),
|
||||
math.Abs(p30Trues[1]-p30Preds[1]),
|
||||
math.Abs(p30Trues[2]-p30Preds[2]),
|
||||
math.Abs(p30Trues[0]-p30Preds[3]),
|
||||
math.Abs(p30Trues[1]-p30Preds[4]),
|
||||
math.Abs(p30Trues[2]-p30Preds[5]),
|
||||
math.Abs(p30Trues[0]-p30Preds[6]),
|
||||
math.Abs(p30Trues[1]-p30Preds[7]),
|
||||
math.Abs(p30Trues[2]-p30Preds[8]),
|
||||
math.Abs(p30Trues[0]-p30Preds[9]),
|
||||
math.Abs(p30Trues[1]-p30Preds[10]),
|
||||
math.Abs(p30Trues[2]-p30Preds[11]))
|
||||
m.logTrue([3]float64{p30Trues[0], p30Trues[1], p30Trues[2]}, p30Preds)
|
||||
}
|
||||
if len(m.hisProbs) >= hisMin {
|
||||
m.hisProbs = m.hisProbs[len(m.hisProbs)-hisMin:]
|
||||
items = append(items, j)
|
||||
his = append(his, m.hisProbs...)
|
||||
m.diff = m.diff[len(m.diff)-diffLen*outDim:]
|
||||
diff = append(diff, m.diff...)
|
||||
m.trues = m.trues[len(m.trues)-diffLen:]
|
||||
inds = append(inds, m.feasBig.RawRowView(feasLen-1)...)
|
||||
inds = append(inds, rawInfo[i*rawInfoDepth:(i+1)*rawInfoDepth]...)
|
||||
info = append(info, (m.pftDoneLong+m.pftUPolLong)/maxOdNum,
|
||||
(m.pftDoneShort+m.pftUPolShort)/maxOdNum,
|
||||
float64(len(j.LongOrders))/maxOdNum, float64(len(j.ShortOrders))/maxOdNum,
|
||||
avgArr(m.trues),
|
||||
)
|
||||
if m.age > 0 && len(j.LongOrders) == 0 && len(j.ShortOrders) == 0 {
|
||||
m.age = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
bSize = len(items)
|
||||
if bSize == 0 {
|
||||
return
|
||||
}
|
||||
trade, err_ := client.Trade(ctx, &biz.ArrMap{
|
||||
Mats: map[string]*biz.NumArr{
|
||||
"his": {Data: his, Shape: []int32{int32(bSize), int32(hisProbLen), int32(outDim)}},
|
||||
"diff": {Data: diff, Shape: []int32{int32(bSize), int32(diffLen), int32(outDim)}},
|
||||
"inds": {Data: inds, Shape: []int32{int32(bSize), int32(indsDepth)}},
|
||||
"info": {Data: info, Shape: []int32{int32(bSize), 5}},
|
||||
},
|
||||
})
|
||||
if err_ != nil {
|
||||
panic(err_)
|
||||
}
|
||||
actions := trade.Mats["actions"].Data
|
||||
for i, j := range items {
|
||||
m, _ := j.More.(*AIMore2)
|
||||
act := int(math.Round(actions[i]))
|
||||
truncated := false
|
||||
if len(j.LongOrders) > 0 || len(j.ShortOrders) > 0 {
|
||||
m.age += 1
|
||||
if m.age >= maxHoldAge {
|
||||
truncated = true
|
||||
}
|
||||
}
|
||||
if (truncated || act == 3 || act == 2) && len(j.LongOrders) > 0 {
|
||||
_ = j.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_long",
|
||||
Dirt: core.OdDirtLong,
|
||||
})
|
||||
}
|
||||
if (truncated || act == 4 || act == 1) && len(j.ShortOrders) > 0 {
|
||||
_ = j.CloseOrders(&strat.ExitReq{
|
||||
Tag: "exit_short",
|
||||
Dirt: core.OdDirtShort,
|
||||
})
|
||||
}
|
||||
if !truncated {
|
||||
if act == 1 && len(j.LongOrders) < int(maxOdNum) {
|
||||
m, _ := j.More.(*AIMore2)
|
||||
_ = j.OpenOrder(&strat.EnterReq{
|
||||
Tag: "long",
|
||||
StopLossVal: m.atr * 5,
|
||||
})
|
||||
} else if act == 2 && len(j.ShortOrders) < int(maxOdNum) {
|
||||
m, _ := j.More.(*AIMore2)
|
||||
_ = j.OpenOrder(&strat.EnterReq{
|
||||
Tag: "short",
|
||||
Short: true,
|
||||
StopLossVal: m.atr * 5,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
OnCheckExit: func(s *strat.StratJob, od *ormo.InOutOrder) *strat.ExitReq {
|
||||
sl := od.GetStopLoss()
|
||||
if sl == nil || sl.Range == 0 {
|
||||
return nil
|
||||
}
|
||||
holdNum := int((s.Env.TimeStop - od.RealEnterMS()) / s.Env.TFMSecs)
|
||||
rate := math.Abs(s.Env.Close.Get(0)-od.InitPrice) / sl.Range
|
||||
if holdNum > nextObsNum && (od.ProfitRate < 0 || rate < 0.3) {
|
||||
// 止损:持仓30个bar && (亏损状态 or 盈利不足最小预期30%)
|
||||
return &strat.ExitReq{
|
||||
Tag: "exit_bad",
|
||||
OrderID: od.ID,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
GetDrawDownExitRate: func(s *strat.StratJob, od *ormo.InOutOrder, maxChg float64) float64 {
|
||||
sl := od.GetStopLoss()
|
||||
if sl == nil || sl.Range == 0 {
|
||||
return 0
|
||||
}
|
||||
bestRate := maxChg * od.InitPrice / sl.Range
|
||||
if bestRate > 1.2 {
|
||||
// 最大盈利是止损区间2倍,以30%回撤止盈
|
||||
return 0.3
|
||||
}
|
||||
return 0
|
||||
},
|
||||
OnOrderChange: func(s *strat.StratJob, od *ormo.InOutOrder, chgType int) {
|
||||
m, _ := s.More.(*AIMore2)
|
||||
if chgType == strat.OdChgExitFill {
|
||||
if od.Short {
|
||||
m.pftDoneShort += od.ProfitRate
|
||||
} else {
|
||||
m.pftDoneLong += od.ProfitRate
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AIMore2) logTrue(trues [3]float64, preds []float64) {
|
||||
val := compareProb(trues, preds)
|
||||
m.trues = append(m.trues, val)
|
||||
}
|
||||
14
rpc_fea/main.go
Normal file
14
rpc_fea/main.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package rpc_fea
|
||||
|
||||
import (
|
||||
"github.com/banbox/banbot/biz"
|
||||
"github.com/banbox/banbot/strat"
|
||||
)
|
||||
|
||||
func init() {
|
||||
biz.FeaGenerators["aifea"] = pubAiFea
|
||||
biz.FeaGenerators["aifea2"] = pubAiFea2
|
||||
biz.FeaGenerators["fea_times"] = pubFeaTimes
|
||||
strat.StratMake["rpc_fea:ai"] = AITrade
|
||||
strat.StratMake["rpc_fea:ai2"] = AITrade2
|
||||
}
|
||||
Reference in New Issue
Block a user