mirror of
https://github.com/datarhei/core.git
synced 2025-09-27 04:16:25 +08:00
119 lines
2.0 KiB
Go
119 lines
2.0 KiB
Go
package average
|
|
|
|
import (
|
|
"container/ring"
|
|
"errors"
|
|
gotime "time"
|
|
|
|
"github.com/datarhei/core/v16/time"
|
|
)
|
|
|
|
type SMA struct {
|
|
ts time.Source
|
|
window int64
|
|
granularity int64
|
|
size int
|
|
last int64
|
|
samples *ring.Ring
|
|
}
|
|
|
|
var ErrWindow = errors.New("window size must be positive")
|
|
var ErrGranularity = errors.New("granularity must be positive")
|
|
var ErrMultiplier = errors.New("window size has to be a multiplier of the granularity size")
|
|
|
|
func NewSMA(window, granularity gotime.Duration) (*SMA, error) {
|
|
if window <= 0 {
|
|
return nil, ErrWindow
|
|
}
|
|
|
|
if granularity <= 0 {
|
|
return nil, ErrGranularity
|
|
}
|
|
|
|
if window <= granularity || window%granularity != 0 {
|
|
return nil, ErrMultiplier
|
|
}
|
|
|
|
s := &SMA{
|
|
ts: &time.StdSource{},
|
|
window: window.Nanoseconds(),
|
|
granularity: granularity.Nanoseconds(),
|
|
}
|
|
|
|
s.init()
|
|
|
|
return s, nil
|
|
}
|
|
|
|
func (s *SMA) init() {
|
|
s.size = int(s.window / s.granularity)
|
|
s.samples = ring.New(s.size)
|
|
|
|
s.Reset()
|
|
|
|
now := s.ts.Now().UnixNano()
|
|
s.last = now - now%s.granularity
|
|
}
|
|
|
|
func (s *SMA) Add(v float64) {
|
|
now := s.ts.Now().UnixNano()
|
|
now -= now % s.granularity
|
|
|
|
n := (now - s.last) / s.granularity
|
|
|
|
if n >= int64(s.samples.Len()) {
|
|
// zero everything
|
|
s.Reset()
|
|
} else {
|
|
for i := n; i > 0; i-- {
|
|
s.samples = s.samples.Next()
|
|
s.samples.Value = float64(0)
|
|
}
|
|
}
|
|
|
|
s.samples.Value = s.samples.Value.(float64) + v
|
|
|
|
s.last = now
|
|
}
|
|
|
|
func (s *SMA) AddAndAverage(v float64) float64 {
|
|
s.Add(v)
|
|
|
|
total := float64(0)
|
|
|
|
s.samples.Do(func(v any) {
|
|
total += v.(float64)
|
|
})
|
|
|
|
return total / float64(s.samples.Len())
|
|
}
|
|
|
|
func (s *SMA) Average() float64 {
|
|
total, samplecount := s.Total()
|
|
|
|
return total / float64(samplecount)
|
|
}
|
|
|
|
func (s *SMA) Reset() {
|
|
n := s.samples.Len()
|
|
|
|
// Initialize the ring buffer with 0 values.
|
|
for i := 0; i < n; i++ {
|
|
s.samples.Value = float64(0)
|
|
s.samples = s.samples.Next()
|
|
}
|
|
}
|
|
|
|
func (s *SMA) Total() (float64, int) {
|
|
// Propagate the ringbuffer
|
|
s.Add(0)
|
|
|
|
total := float64(0)
|
|
|
|
s.samples.Do(func(v any) {
|
|
total += v.(float64)
|
|
})
|
|
|
|
return total, s.samples.Len()
|
|
}
|