blockchain/internal/lib/strategy/rsi.go

63 lines
1.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)))
}