blockchain/internal/lib/strategy/bool.go

137 lines
3.5 KiB
Go
Raw Permalink Normal View History

2025-08-20 12:35:31 +00:00
package strategy
import (
"github.com/shopspring/decimal"
"math"
)
/************** O(1) 滾動標準差Welford + ring buffer **************/
// 滾動統計(固定窗寬):維持 mean 與 M2平方離差和
// 支援「移除最舊樣本」+「新增最新樣本」的 O(1) 更新
type rollingWelford struct {
n int // 窗寬
buf []decimal.Decimal // 環狀緩衝(固定長度 n
head int // 下一個覆寫位置(最舊元素位置)
size int // 當前已填入數量(<= n
mean decimal.Decimal // 目前視窗內的平均值
m2 decimal.Decimal // 目前視窗內的平方離差和(∑(x-mean)^2
ready bool // 是否已填滿 n
}
func newRollingWelford(n int) *rollingWelford {
if n <= 0 {
panic("rollingWelford window must be > 0")
}
return &rollingWelford{
n: n,
buf: make([]decimal.Decimal, n),
}
}
// add 一個樣本(標準 Welford 加法)
func (rw *rollingWelford) add(x decimal.Decimal) {
// n_old = size, n_new = size+1
nOld := decimal.NewFromInt(int64(rw.size))
nNew := nOld.Add(decimal.NewFromInt(1))
delta := x.Sub(rw.mean)
meanNew := rw.mean.Add(delta.Div(nNew))
delta2 := x.Sub(meanNew)
rw.m2 = rw.m2.Add(delta.Mul(delta2))
rw.mean = meanNew
if rw.size < rw.n {
rw.size++
if rw.size == rw.n {
rw.ready = true
}
}
}
// remove 一個樣本Welford 反向移除公式)
func (rw *rollingWelford) remove(x decimal.Decimal) {
if rw.size <= 0 {
return
}
// n_old = size, n_new = size-1
nOld := decimal.NewFromInt(int64(rw.size))
nNew := nOld.Sub(decimal.NewFromInt(1))
if nNew.LessThanOrEqual(decimal.Zero) {
// 視窗將清空
rw.size = 0
rw.mean = decimal.Zero
rw.m2 = decimal.Zero
rw.ready = false
return
}
delta := x.Sub(rw.mean) // (x - mean_old)
meanNew := rw.mean.Sub(delta.Div(nNew)) // mean_new = mean_old - delta / n_new
// M2_new = M2_old - delta*(x - mean_new)
rw.m2 = rw.m2.Sub(delta.Mul(x.Sub(meanNew)))
rw.mean = meanNew
rw.size--
rw.ready = rw.size == rw.n // 正常來說 size==n-1移除後會在 add 後再回到 n
}
// pushO(1) 更新(移除最舊 + 新增最新)
func (rw *rollingWelford) push(x decimal.Decimal) {
if rw.size == rw.n {
old := rw.buf[rw.head]
rw.remove(old)
}
rw.buf[rw.head] = x
rw.head = (rw.head + 1) % rw.n
rw.add(x)
}
// mid() = 平均std() = sqrt(variance)
// 這裡採「母體方差」分母 = n與很多圖表預設一致如需樣本用 n-1
func (rw *rollingWelford) mid() decimal.Decimal { return rw.mean }
func (rw *rollingWelford) std() decimal.Decimal {
if !rw.ready || rw.size == 0 {
return decimal.Zero
}
nDec := decimal.NewFromInt(int64(rw.n))
variance := rw.m2.Div(nDec)
f, _ := variance.Float64()
return decimal.NewFromFloat(math.Sqrt(f))
}
/************** BollWelford 版本) **************/
type BollW struct {
rw *rollingWelford
}
type BollWOut struct {
Mid, Upper, Lower, Std decimal.Decimal
Ready bool
}
func NewBollW(n int) *BollW {
return &BollW{rw: newRollingWelford(n)}
}
// Push 輸入收盤價 close 與 k 倍標準差(典型 2.0
// 回傳:上下軌、均線、標準差、是否就緒(前 n 根為 false
func (b *BollW) Push(close, k decimal.Decimal) BollWOut {
b.rw.push(close)
if !b.rw.ready {
return BollWOut{Ready: false}
}
mid := b.rw.mid()
std := b.rw.std()
upper := mid.Add(k.Mul(std))
lower := mid.Sub(k.Mul(std))
return BollWOut{
Mid: mid,
Upper: upper,
Lower: lower,
Std: std,
Ready: true,
}
}