package indicators // MACDResult contient MACD line, signal line et histogramme. type MACDResult struct { MACD float64 Signal float64 Histogram float64 } // MACD calcule le Moving Average Convergence Divergence (12/26/9 standard). // Retourne zéro-value si pas assez de données. func MACD(closes []float64) MACDResult { return MACDCustom(closes, 12, 26, 9) } func MACDCustom(closes []float64, fast, slow, signal int) MACDResult { if len(closes) < slow+signal { return MACDResult{} } emaFast := emaSlice(closes, fast) emaSlow := emaSlice(closes, slow) // Aligner les deux séries (emaSlow est plus courte) offset := len(emaFast) - len(emaSlow) macdLine := make([]float64, len(emaSlow)) for i := range emaSlow { macdLine[i] = emaFast[offset+i] - emaSlow[i] } if len(macdLine) < signal { return MACDResult{} } signalLine := emaSlice(macdLine, signal) last := macdLine[len(macdLine)-1] sig := signalLine[len(signalLine)-1] return MACDResult{ MACD: last, Signal: sig, Histogram: last - sig, } } // SMA calcule la moyenne mobile simple sur les n dernières valeurs. func SMA(closes []float64, period int) float64 { if len(closes) < period { return 0 } slice := closes[len(closes)-period:] sum := 0.0 for _, v := range slice { sum += v } return sum / float64(period) } // AvgVolume calcule le volume moyen sur les n dernières barres. func AvgVolume(volumes []int64, period int) int64 { if len(volumes) < period { period = len(volumes) } if period == 0 { return 0 } slice := volumes[len(volumes)-period:] var sum int64 for _, v := range slice { sum += v } return sum / int64(period) } func emaSlice(data []float64, period int) []float64 { if len(data) < period { return nil } k := 2.0 / float64(period+1) // Première valeur = SMA des `period` premières sum := 0.0 for i := 0; i < period; i++ { sum += data[i] } ema := make([]float64, 0, len(data)-period+1) ema = append(ema, sum/float64(period)) for i := period; i < len(data); i++ { ema = append(ema, data[i]*k+ema[len(ema)-1]*(1-k)) } return ema }