746 lines
20 KiB
Go
746 lines
20 KiB
Go
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
|
||
}
|