-
Notifications
You must be signed in to change notification settings - Fork 6
/
sma.go
64 lines (58 loc) · 2.12 KB
/
sma.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package movavg
// SMA is a calculator of Simple Moving Average:
// https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
//
// It is as precise, as float64 is: https://golang.org/pkg/builtin/#float64 (good performance, not-so-good precision).
//
// It is not thread-safe, if you need to access it from multiple goroutines - secure it with ThreadSafe(...).
//
// It is safe to use even without adding values (then Avg() will return zero),
// or without filling entire window (then average of currently added values will be returned).
//
// To initialize calculator with current average value, simply Add(...) this value.
type SMA struct {
window int // Simple Moving Average window (number of latest values to calculate average value)
vals []float64 // stored values, circular list
n int // number of actually stored values (vals slice usage)
i int // last-set value index; first-set (oldest) value is (i+1)%window when n == window
avg float64 // current Simple Moving Average value
}
// NewSMA constructs new Simple Moving Average calculator.
// Window arg must be >= 1.
func NewSMA(window int) *SMA {
if window <= 0 {
panic("movavg.NewSMA: window should be > 0")
}
return &SMA{
window: window,
vals: make([]float64, window),
}
}
// Add recalculates Simple Moving Average value and returns it.
func (a *SMA) Add(v float64) float64 {
if a.n == a.window {
// filled window - most frequent case:
// https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
a.i = (a.i + 1) % a.window
a.avg += (v - a.vals[a.i]) / float64(a.n)
a.vals[a.i] = v
} else if a.n != 0 {
// partially-filled window - second most frequent case:
// https://en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average
a.i = (a.i + 1) % a.window
a.avg = (v + float64(a.n)*a.avg) / float64(a.n+1)
a.vals[a.i] = v
a.n++
} else {
// empty window - least frequent case (occurs only once, on first value added):
// simply assign given value as current average:
a.avg = v
a.vals[0] = v
a.n = 1
}
return a.avg
}
// Avg returns current Simple Moving Average value.
func (a *SMA) Avg() float64 {
return a.avg
}