blockchain/internal/lib/strategy/rsi.go

63 lines
1.8 KiB
Go
Raw Normal View History

2025-08-20 12:35:31 +00:00
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)))
}