blockchain/internal/lib/strategy/sma.go

104 lines
2.2 KiB
Go
Raw 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 strategy
import (
"sync/atomic"
"github.com/shopspring/decimal"
)
// =========================
// SMA固定窗寬單寫多讀 + atomic 快照)
// =========================
// smaCore單 writer 的核心計算ring buffer + 滑動平均)
type smaCore struct {
window uint
// 狀態(單 writer
buf []decimal.Decimal // ring buffer
head int // 下一個覆蓋位置
size uint // 目前填充數量(<= window
sum decimal.Decimal
ready bool
}
func newSMACore(window uint) *smaCore {
if window == 0 {
panic("SMA window must be > 0")
}
return &smaCore{
window: window,
buf: make([]decimal.Decimal, window),
}
}
// Push 只能被**單一** goroutinewriter呼叫。
func (s *smaCore) Push(x decimal.Decimal) (decimal.Decimal, bool) {
// 填充期
if s.size < s.window {
s.buf[s.head] = x
s.sum = s.sum.Add(x)
s.head = (s.head + 1) % int(s.window)
s.size++
if s.size == s.window {
s.ready = true
}
return s.sum.Div(decimal.NewFromInt(int64(s.size))), s.ready
}
// 滿窗:移除最舊,加入最新
old := s.buf[s.head]
s.buf[s.head] = x
s.head = (s.head + 1) % int(s.window)
s.sum = s.sum.Sub(old).Add(x)
avg := s.sum.Div(decimal.NewFromInt(int64(s.window)))
return avg, true
}
// SMASnapshot 對外發佈的不可變快照(多 reader 零鎖 Load
type SMASnapshot struct {
Window uint
Value decimal.Decimal
Ready bool
LastInput decimal.Decimal
Size uint // 當前填充數量(<=Window
}
// SMA 對外使用的快照發佈器(單寫多讀)
type SMA struct {
core *smaCore
last decimal.Decimal
snap atomic.Value // holds SMASnapshot
}
func NewSMA(window uint) *SMA {
s := &SMA{core: newSMACore(window)}
s.snap.Store(SMASnapshot{Window: window})
return s
}
// Update 僅允許**單一 writer**呼叫
func (s *SMA) Update(x decimal.Decimal) SMASnapshot {
val, ready := s.core.Push(x)
s.last = x
ss := SMASnapshot{
Window: s.core.window,
Value: val,
Ready: ready,
LastInput: x,
Size: s.core.size,
}
s.snap.Store(ss)
return ss
}
// Load 多 reader 零鎖讀最新快照
func (s *SMA) Load() SMASnapshot {
return s.snap.Load().(SMASnapshot)
}