90 lines
2.4 KiB
Go
90 lines
2.4 KiB
Go
|
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)
|
|||
|
}
|