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

745
rpc_fea/common.go Normal file
View 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
}