Files
banstagy/com/exm.go
2026-08-24 20:53:21 +08:00

136 lines
3.0 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 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
}