package strategy import "github.com/shopspring/decimal" // RSI 使用 Wilder 的平均漲跌(非簡單平均),更貼近交易軟體常見計法 type RSI struct { n int prevC decimal.Decimal // 前一根收盤價 initCount int // 初始化用:先累積前 n 根的總漲/總跌 avgGain decimal.Decimal // 平滑後的平均上漲 avgLoss decimal.Decimal // 平滑後的平均下跌 ok bool // 是否有 prevC } func NewRSI(n int) *RSI { return &RSI{n: n} } // Push 餵入一根K線,回傳 (RSI值, 是否就緒) // 注意:前 n 根會回傳就緒=false;之後才可信 func (r *RSI) Push(c CandleForStrategy) (decimal.Decimal, bool) { if !r.ok { r.prevC = c.C r.ok = true return decimal.Zero, false } // 價差 chg := c.C.Sub(r.prevC) r.prevC = c.C // 區分上漲與下跌 gain := decimal.Max(chg, decimal.Zero) loss := decimal.Max(chg.Neg(), decimal.Zero) // 初始化階段:先把前 n 根的平均值建好 if r.initCount < r.n { r.avgGain = r.avgGain.Add(gain) r.avgLoss = r.avgLoss.Add(loss) r.initCount++ if r.initCount == r.n { r.avgGain = r.avgGain.Div(decimal.NewFromInt(int64(r.n))) r.avgLoss = r.avgLoss.Div(decimal.NewFromInt(int64(r.n))) return r.calc(), true } return decimal.Zero, false } // Wilder 平滑:新的平均 = (舊平均*(n-1) + 當期值) / n nDec := decimal.NewFromInt(int64(r.n)) r.avgGain = (r.avgGain.Mul(nDec.Sub(decimal.NewFromInt(1))).Add(gain)).Div(nDec) r.avgLoss = (r.avgLoss.Mul(nDec.Sub(decimal.NewFromInt(1))).Add(loss)).Div(nDec) return r.calc(), true } func (r *RSI) calc() decimal.Decimal { if r.avgLoss.IsZero() { return decimal.NewFromInt(100) // 沒有下跌時,RSI=100 } rs := r.avgGain.Div(r.avgLoss) one := decimal.NewFromInt(1) hundred := decimal.NewFromInt(100) return hundred.Sub(hundred.Div(one.Add(rs))) }