package strategy import ( "sync/atomic" "github.com/shopspring/decimal" ) // ========================= // MACD(單寫;快照多讀) // fastEMA / slowEMA 吃「收盤價」;signalEMA 吃「MACDLine」 // 與 EMA/SMA 一致風格:Update()(單寫)、Load()(多讀) // ========================= // MACDSnapshot 對外發佈的不可變快照 type MACDSnapshot struct { Params [3]uint // (fast, slow, signal) LastPrice decimal.Decimal // 最近一次喂入的收盤價 MACDLine decimal.Decimal // DIF = fastEMA - slowEMA SignalLine decimal.Decimal // DEA = EMA(MACDLine, signalPeriod) Histogram decimal.Decimal // Hist = MACDLine - SignalLine Ready bool // 三條線皆 ready 才會 true } // MACD 快照發佈器(單寫多讀) type MACD struct { // 內部用你已完成的 EMA(其本身就含 SMA-seed 與 ready 狀態) fastEMA *EMA slowEMA *EMA signalEMA *EMA fastPeriod, slowPeriod, signalPeriod uint last decimal.Decimal // 只作紀錄(debug/觀察用) snap atomic.Value // holds MACDSnapshot } // NewMACD 建立 MACD(預設 12,26,9;也可自訂) func NewMACD(fast, slow, signal uint) *MACD { if !(fast > 0 && slow > 0 && signal > 0) { panic("MACD periods must be > 0") } if fast >= slow { panic("MACD requires fast < slow (e.g., 12 < 26)") } m := &MACD{ fastEMA: NewEMA(fast), slowEMA: NewEMA(slow), signalEMA: NewEMA(signal), fastPeriod: fast, slowPeriod: slow, signalPeriod: signal, } // 初始化一個空快照 m.snap.Store(MACDSnapshot{Params: [3]uint{fast, slow, signal}}) return m } // Update 僅供單一 writer 呼叫;回傳並發佈最新快照 func (m *MACD) Update(close decimal.Decimal) MACDSnapshot { // 先用 EMA(你已改好的版本)各自更新快、慢線 fast := m.fastEMA.Update(close) slow := m.slowEMA.Update(close) macdLine := fast.Value.Sub(slow.Value) // 信號線吃的是 MACDLine(非收盤價) sig := m.signalEMA.Update(macdLine) ready := fast.Ready && slow.Ready && sig.Ready snap := MACDSnapshot{ Params: [3]uint{m.fastPeriod, m.slowPeriod, m.signalPeriod}, LastPrice: close, MACDLine: macdLine, SignalLine: sig.Value, Histogram: macdLine.Sub(sig.Value), Ready: ready, } m.last = close m.snap.Store(snap) return snap } // Load 多 goroutine 零鎖讀取最新快照 func (m *MACD) Load() MACDSnapshot { return m.snap.Load().(MACDSnapshot) }