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 只能被**單一** goroutine(writer)呼叫。 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) }