github.com/diadata-org/diadata@v1.4.593/internal/pkg/filtersBlockService/FilterMA.go (about) 1 package filters 2 3 import ( 4 "math" 5 "strconv" 6 "time" 7 8 "github.com/diadata-org/diadata/pkg/dia" 9 models "github.com/diadata-org/diadata/pkg/model" 10 log "github.com/sirupsen/logrus" 11 ) 12 13 // FilterMA is the struct for a moving average filter implementing 14 // the Filter interface. 15 type FilterMA struct { 16 asset dia.Asset 17 exchange string 18 currentTime time.Time 19 prices []float64 20 volumes []float64 21 lastTrade dia.Trade 22 memory int 23 value float64 24 modified bool 25 filterName string 26 nativeDenomination bool 27 //max float64 28 min float64 29 } 30 31 // NewFilterMA returns a moving average filter. 32 // @currentTime is the begin time of the filtersBlock. 33 func NewFilterMA(asset dia.Asset, exchange string, currentTime time.Time, memory int, nativeDenomination bool) *FilterMA { 34 filter := &FilterMA{ 35 asset: asset, 36 exchange: exchange, 37 prices: []float64{}, 38 volumes: []float64{}, 39 currentTime: currentTime, 40 memory: memory, 41 filterName: "MA" + strconv.Itoa(memory), 42 min: -1, 43 nativeDenomination: nativeDenomination, 44 } 45 return filter 46 } 47 48 func (filter *FilterMA) Compute(trade dia.Trade) { 49 filter.compute(trade) 50 } 51 52 func (filter *FilterMA) compute(trade dia.Trade) { 53 filter.modified = true 54 if filter.lastTrade != (dia.Trade{}) { 55 if trade.Time.Before(filter.currentTime) { 56 log.Errorln("FilterMA: Ignoring Trade out of order ", filter.currentTime, trade.Time) 57 return 58 } 59 } 60 filter.fill(trade) 61 filter.lastTrade = trade 62 } 63 64 // fill fills up the 120 seconds slots with trades. 65 func (filter *FilterMA) fill(trade dia.Trade) { 66 // filter.currentTime is the timestamp of the last filled trade. 67 // It is initialized with begin time of tradesblock upon creation of the filter. 68 diff := int(trade.Time.Sub(filter.currentTime).Seconds()) 69 if diff >= 1 { 70 for diff >= 1 { 71 filter.processDataPoint(trade) 72 diff-- 73 } 74 } else { 75 if diff == 0.0 { 76 if len(filter.prices) >= 1 { 77 /// Remove latest data point and update with newer 78 filter.prices = filter.prices[:len(filter.prices)-1] 79 filter.volumes = filter.volumes[:len(filter.volumes)-1] 80 } 81 } 82 filter.processDataPoint(trade) 83 } 84 filter.currentTime = time.Unix(int64(trade.Time.Unix()), 0) 85 } 86 87 func (filter *FilterMA) processDataPoint(trade dia.Trade) { 88 /// first remove extra value from buffer if already full 89 if len(filter.prices) >= filter.memory { 90 filter.prices = filter.prices[0 : filter.memory-1] 91 filter.volumes = filter.volumes[0 : filter.memory-1] 92 } 93 if !filter.nativeDenomination { 94 filter.prices = append(filter.prices, trade.EstimatedUSDPrice) 95 } else { 96 filter.prices = append(filter.prices, trade.Price) 97 } 98 99 filter.volumes = append([]float64{trade.Volume}, filter.volumes...) 100 } 101 102 func (filter *FilterMA) FinalCompute(t time.Time) float64 { 103 return filter.finalCompute(t) 104 } 105 106 func (filter *FilterMA) finalCompute(t time.Time) float64 { 107 if filter.lastTrade == (dia.Trade{}) { 108 return 0.0 109 } 110 filter.fill(filter.lastTrade) 111 var totalVolume float64 112 var totalPrice float64 113 for priceIndex, price := range filter.prices { 114 totalPrice += price * math.Abs(filter.volumes[priceIndex]) 115 totalVolume += math.Abs(filter.volumes[priceIndex]) 116 } 117 if filter.asset.Symbol == "USDT" || filter.asset.Symbol == "USDC" { 118 var nonweightedPrice float64 119 for _, price := range filter.prices { 120 nonweightedPrice += price 121 } 122 } 123 if totalVolume > 0 { 124 filter.value = totalPrice / totalVolume 125 126 } 127 if len(filter.prices) > 0 && len(filter.volumes) > 0 { 128 if !filter.nativeDenomination { 129 filter.prices = []float64{filter.lastTrade.EstimatedUSDPrice} 130 } else { 131 filter.prices = []float64{filter.lastTrade.Price} 132 } 133 filter.volumes = []float64{filter.lastTrade.Volume} 134 } 135 return filter.value 136 } 137 138 func (filter *FilterMA) FilterPointForBlock() *dia.FilterPoint { 139 return &dia.FilterPoint{ 140 Asset: filter.asset, 141 Value: filter.value, 142 Name: "MA" + strconv.Itoa(filter.memory), 143 Time: filter.currentTime, 144 } 145 } 146 147 func (filter *FilterMA) filterPointForBlock() *dia.FilterPoint { 148 if filter.exchange != "" || filter.filterName != dia.FilterKing { 149 return nil 150 } 151 return &dia.FilterPoint{ 152 Asset: filter.asset, 153 Value: filter.value, 154 Name: "MA" + strconv.Itoa(filter.memory), 155 Time: filter.currentTime, 156 } 157 } 158 159 func (filter *FilterMA) save(ds models.Datastore) error { 160 if filter.modified { 161 filter.modified = false 162 err := ds.SetFilter(filter.filterName, filter.asset, filter.exchange, filter.value, filter.currentTime) 163 if err != nil { 164 log.Errorln("FilterMA: Error:", err) 165 } 166 return err 167 } 168 return nil 169 }