Files
banstagy/rpc_fea/common.go
2026-08-24 20:53:21 +08:00

746 lines
20 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}