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 }