package pricing import ( "math" "sort" "time" "git.mchus.pro/mchus/priceforge/internal/repository" ) // CalculateMedian returns the median of prices func CalculateMedian(prices []float64) float64 { if len(prices) == 0 { return 0 } sorted := make([]float64, len(prices)) copy(sorted, prices) sort.Float64s(sorted) n := len(sorted) if n%2 == 0 { return (sorted[n/2-1] + sorted[n/2]) / 2 } return sorted[n/2] } // CalculateAverage returns the arithmetic mean of prices func CalculateAverage(prices []float64) float64 { if len(prices) == 0 { return 0 } var sum float64 for _, p := range prices { sum += p } return sum / float64(len(prices)) } // CalculateWeightedMedian calculates median with exponential decay weights // More recent prices have higher weight func CalculateWeightedMedian(points []repository.PricePoint, decayDays int) float64 { if len(points) == 0 { return 0 } type weightedPrice struct { price float64 weight float64 } now := time.Now() weighted := make([]weightedPrice, len(points)) var totalWeight float64 for i, p := range points { daysSince := now.Sub(p.Date).Hours() / 24 // weight = e^(-days / decay_days) weight := math.Exp(-daysSince / float64(decayDays)) weighted[i] = weightedPrice{price: p.Price, weight: weight} totalWeight += weight } // Sort by price sort.Slice(weighted, func(i, j int) bool { return weighted[i].price < weighted[j].price }) // Find weighted median targetWeight := totalWeight / 2 var cumulativeWeight float64 for _, wp := range weighted { cumulativeWeight += wp.weight if cumulativeWeight >= targetWeight { return wp.price } } return weighted[len(weighted)-1].price } // CalculatePercentile calculates the nth percentile of prices func CalculatePercentile(prices []float64, percentile float64) float64 { if len(prices) == 0 { return 0 } sorted := make([]float64, len(prices)) copy(sorted, prices) sort.Float64s(sorted) index := (percentile / 100) * float64(len(sorted)-1) lower := int(math.Floor(index)) upper := int(math.Ceil(index)) if lower == upper { return sorted[lower] } fraction := index - float64(lower) return sorted[lower]*(1-fraction) + sorted[upper]*fraction } // CalculateStdDev calculates standard deviation func CalculateStdDev(prices []float64) float64 { if len(prices) < 2 { return 0 } mean := CalculateAverage(prices) var sumSquares float64 for _, p := range prices { diff := p - mean sumSquares += diff * diff } return math.Sqrt(sumSquares / float64(len(prices)-1)) }