github.com/diadata-org/diadata@v1.4.593/internal/pkg/filtersBlockService/Filter.go (about) 1 package filters 2 3 import ( 4 "errors" 5 "math" 6 "sort" 7 "time" 8 9 "github.com/diadata-org/diadata/pkg/dia" 10 models "github.com/diadata-org/diadata/pkg/model" 11 ) 12 13 // Filter interface defines a filter's methods processing trades from the tradesBlockService. 14 type Filter interface { 15 compute(trade dia.Trade) 16 finalCompute(t time.Time) float64 17 filterPointForBlock() *dia.FilterPoint 18 save(ds models.Datastore) error 19 } 20 21 func RemoveOutliers(samples []float64, scale float64) ([]float64, []int) { 22 return removeOutliersScaled(samples, scale) 23 } 24 25 func removeOutliers(samples []float64) ([]float64, []int) { 26 return removeOutliersScaled(samples, float64(1.5)) 27 } 28 29 // RemoveOutliersScaled Cleans a data set it accordance to the acceptable range within interquartile range. 30 // It returns the cleaned data slice plus a slice of lower and upper index bounds. 31 func removeOutliersScaled(samples []float64, scale float64) ([]float64, []int) { 32 var indexBounds []int 33 if len(samples) == 0 || len(samples) == 1 { 34 return samples, indexBounds 35 } 36 Q1, Q3 := computeQuartiles(samples) 37 IQR := Q3 - Q1 38 lowerBound := Q1 - scale*IQR 39 upperBound := Q3 + scale*IQR 40 lowerIndex := 0 41 upperIndex := len(samples) 42 for index, value := range samples { 43 if value < lowerBound { 44 lowerIndex = index + 1 45 } else if value > upperBound { 46 upperIndex = index 47 break 48 } 49 } 50 indexBounds = append(indexBounds, lowerIndex) 51 indexBounds = append(indexBounds, upperIndex) 52 return samples[lowerIndex:upperIndex], indexBounds 53 } 54 55 // computeMean returns the weighted mean of @samples with @weights. 56 // Special case of non-weighted mean is obtained by setting weights to constant 1-slice. 57 func computeMean(samples []float64, weights []float64) (mean float64, err error) { 58 var totalPrice float64 59 var totalVolume float64 60 length := float64(len(samples)) 61 if length == 0 { 62 return 0, nil 63 } 64 if length != float64(len(weights)) { 65 return 0, errors.New("computeMean: samples and weights not of same size") 66 } 67 for index, s := range samples { 68 totalPrice += s * math.Abs(weights[index]) 69 totalVolume += math.Abs(weights[index]) 70 } 71 mean = totalPrice / totalVolume 72 return 73 } 74 75 // ------------ Auxilliary functions for removeOutliers ------------- 76 77 func computeQuartiles(samples []float64) (Q1 float64, Q3 float64) { 78 sort.Float64s(samples) 79 var length = len(samples) 80 if length > 0 { 81 if length%2 == 0 { 82 Q1 = computeMedian(samples[0 : length/2]) 83 Q3 = computeMedian(samples[length/2 : length]) 84 } else { 85 Q1 = computeMedian(samples[0:int(float64(length/2))]) 86 Q3 = computeMedian(samples[int(float64(length/2))+1 : length]) 87 } 88 } 89 return 90 } 91 92 func computeMedian(samples []float64) (median float64) { 93 var length = len(samples) 94 if length > 0 { 95 sort.Float64s(samples) 96 if length%2 == 0 { 97 median = (samples[length/2-1] + samples[length/2]) / 2 98 } else { 99 median = samples[(length+1)/2-1] 100 } 101 } 102 return 103 }