136 lines
3.0 KiB
Go
136 lines
3.0 KiB
Go
package com
|
||
|
||
import (
|
||
"math"
|
||
"slices"
|
||
)
|
||
|
||
type ExmItem struct {
|
||
BarNum int
|
||
TimeMS int64
|
||
Value float64
|
||
Valid bool
|
||
}
|
||
|
||
type ExmTrace struct {
|
||
Hist []*ExmItem
|
||
Expect int // 1寻找极大值,-1寻找极小值
|
||
PrevVals []float64 // 前一个值
|
||
PrevDist float64 // 前两个极值的距离
|
||
distList []float64 // 前面极值距离,保留最近3个
|
||
}
|
||
|
||
/*
|
||
Update
|
||
val: 当前指标值
|
||
pNum: 前一个索引
|
||
ptime:前一个时间戳
|
||
*/
|
||
func (m *ExmTrace) Update(val float64, pNum int, ptime int64) bool {
|
||
if math.IsNaN(val) {
|
||
return false
|
||
}
|
||
if len(m.PrevVals) < 2 {
|
||
// 至少积累2个
|
||
if len(m.PrevVals) > 0 && m.PrevVals[len(m.PrevVals)-1] == val {
|
||
// 和前一个值相同,跳过
|
||
return false
|
||
}
|
||
m.PrevVals = append(m.PrevVals, val)
|
||
return false
|
||
}
|
||
// 前1个,前2个
|
||
p1, p2 := m.PrevVals[1], m.PrevVals[0]
|
||
if p1 == val {
|
||
// 当前值无效
|
||
return false
|
||
}
|
||
// 更新
|
||
m.PrevVals = append(m.PrevVals, val)
|
||
m.PrevVals = m.PrevVals[len(m.PrevVals)-2:]
|
||
pSub := p1 - p2
|
||
sub := p1 - val
|
||
var last *ExmItem
|
||
if len(m.Hist) > 0 {
|
||
last = m.Hist[len(m.Hist)-1]
|
||
}
|
||
getMinDist := func() float64 {
|
||
if last == nil {
|
||
return math.Abs(p1) * 0.3
|
||
}
|
||
if m.PrevDist != 0 {
|
||
return m.PrevDist * 0.3
|
||
}
|
||
return max(math.Abs(last.Value), math.Abs(p1)) * 0.3
|
||
}
|
||
if pSub*sub <= 0 {
|
||
// 非极大值或极小值
|
||
if last != nil && !last.Valid && (val-last.Value)*float64(m.Expect) > getMinDist() {
|
||
// 检查最后一个是否有效
|
||
last.Valid = true
|
||
if len(m.Hist) > 1 {
|
||
dist := math.Abs(m.Hist[len(m.Hist)-2].Value - m.Hist[len(m.Hist)-1].Value)
|
||
m.distList = append(m.distList, dist)
|
||
if len(m.distList) > 3 {
|
||
m.distList = m.distList[len(m.distList)-3:]
|
||
}
|
||
m.PrevDist = slices.Max(m.distList)
|
||
}
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
valid := 0
|
||
if sub > 0 {
|
||
if m.Expect == 0 || m.Expect == 1 {
|
||
// 新增极大值
|
||
if last == nil || p1-last.Value >= getMinDist() {
|
||
valid = -1
|
||
}
|
||
} else if p1 > last.Value {
|
||
// 更新极大值
|
||
valid = -1
|
||
}
|
||
} else if sub < 0 {
|
||
if m.Expect == 0 || m.Expect == -1 {
|
||
// 新增极小值
|
||
if last == nil || last.Value-p1 >= getMinDist() {
|
||
valid = 1
|
||
}
|
||
} else if p1 < last.Value {
|
||
// 更新极小值
|
||
valid = 1
|
||
}
|
||
}
|
||
if valid != 0 {
|
||
exm := &ExmItem{
|
||
TimeMS: ptime,
|
||
BarNum: pNum,
|
||
Value: p1,
|
||
}
|
||
if m.Expect != valid {
|
||
m.Hist = append(m.Hist, exm)
|
||
//log.Info("add exm", zap.String("date", btime.ToDateStr(ptime, "")),
|
||
// zap.Float64("val", p1))
|
||
} else {
|
||
m.Hist[len(m.Hist)-1] = exm
|
||
//log.Info("update exm", zap.String("date", btime.ToDateStr(ptime, "")),
|
||
// zap.Float64("val", p1))
|
||
}
|
||
if len(m.Hist) > 2 {
|
||
dist := math.Abs(m.Hist[len(m.Hist)-3].Value - m.Hist[len(m.Hist)-2].Value)
|
||
if len(m.distList) == 0 || dist != m.distList[len(m.distList)-1] {
|
||
m.distList = append(m.distList, dist)
|
||
if len(m.distList) > 3 {
|
||
m.distList = m.distList[len(m.distList)-3:]
|
||
}
|
||
m.PrevDist = slices.Max(m.distList)
|
||
}
|
||
}
|
||
m.Expect = valid
|
||
m.PrevVals = nil
|
||
return true
|
||
}
|
||
return false
|
||
}
|