github.com/diadata-org/diadata@v1.4.593/pkg/model/filters.go (about) 1 package models 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "strconv" 8 "time" 9 10 "github.com/diadata-org/diadata/pkg/dia" 11 "github.com/go-redis/redis" 12 clientInfluxdb "github.com/influxdata/influxdb1-client/v2" 13 ) 14 15 // SetFilter stores a filter point 16 func (datastore *DB) SetFilter(filter string, asset dia.Asset, exchange string, value float64, t time.Time) error { 17 // return datastore.SaveFilterInflux(filter, asset, exchange, value, t) 18 err := datastore.SaveFilterInflux(filter, asset, exchange, value, t) 19 if err != nil { 20 return err 21 } 22 err = datastore.setZSETValue(getKeyFilterZSET(getKey(filter, asset, exchange)), value, t.Unix(), BiggestWindow) 23 if err != nil { 24 return err 25 } 26 return err 27 } 28 29 func (datastore *DB) GetFilterPointsAsset(filter string, exchange string, address string, blockchain string, starttime time.Time, endtime time.Time) (*Points, error) { 30 31 exchangeQuery := "AND exchange='" + exchange + "' " 32 33 q := fmt.Sprintf("SELECT time,address,blockchain,exchange,filter,symbol,value FROM %s"+ 34 " WHERE filter='%s' %s AND address='%s' and blockchain='%s' AND time>%d and time<=%d ORDER BY DESC", 35 influxDbFiltersTable, filter, exchangeQuery, address, blockchain, starttime.UnixNano(), endtime.UnixNano()) 36 37 res, err := queryInfluxDB(datastore.influxClient, q) 38 if err != nil { 39 log.Errorln("GetFilterPoints", err) 40 } 41 42 log.Info("GetFilterPoints query: ", q, " returned ", len(res)) 43 44 return &Points{ 45 DataPoints: res, 46 }, err 47 } 48 49 // GetFilterPoints returns filter points from either a specific exchange or all exchanges. 50 // symbol is mapped to the underlying asset with biggest market cap. 51 func (datastore *DB) GetFilterPoints(filter string, exchange string, symbol string, scale string, starttime time.Time, endtime time.Time) (*Points, error) { 52 relDB, err := NewRelDataStore() 53 if err != nil { 54 log.Errorln("NewDataStore:", err) 55 } 56 // First get asset with @symbol with largest volume. 57 // topAsset, err := db.GetTopAssetByVolume(symbol, relDB) 58 sortedAssets, err := relDB.GetTopAssetByVolume(symbol) 59 if err != nil { 60 log.Error(err) 61 return &Points{}, err 62 } 63 if len(sortedAssets) == 0 { 64 return nil, errors.New("no traded assets found") 65 } 66 topAsset := sortedAssets[0] 67 68 exchangeQuery := "and exchange='" + exchange + "' " 69 table := "" 70 // 5m 30m 1h 4h 1d 1w 71 if scale != "" { 72 if filter == "VOL120" { 73 table = "a_year.filters_sum_" 74 } else { 75 table = "a_year.filters_mean_" 76 } 77 table = table + scale 78 } else { 79 table = influxDbFiltersTable 80 } 81 82 q := fmt.Sprintf("SELECT time,exchange,filter,symbol,value FROM %s"+ 83 " WHERE filter='%s' %sand address='%s' and blockchain='%s' and time>%d and time<%d ORDER BY DESC", 84 table, filter, exchangeQuery, topAsset.Address, topAsset.Blockchain, starttime.UnixNano(), endtime.UnixNano()) 85 86 res, err := queryInfluxDB(datastore.influxClient, q) 87 if err != nil { 88 log.Errorln("GetFilterPoints", err) 89 } 90 91 log.Info("GetFilterPoints query: ", q, " returned ", len(res)) 92 93 return &Points{ 94 DataPoints: res, 95 }, err 96 } 97 98 func (datastore *DB) GetFilter(filter string, topAsset dia.Asset, scale string, starttime time.Time, endtime time.Time) ([]dia.FilterPoint, error) { 99 var allFilters []dia.FilterPoint 100 table := "" 101 // 5m 30m 1h 4h 1d 1w 102 if scale != "" { 103 if filter == "VOL120" { 104 table = "a_year.filters_sum_" 105 } else { 106 table = "a_year.filters_mean_" 107 } 108 table = table + scale 109 } else { 110 table = influxDbFiltersTable 111 } 112 113 q := fmt.Sprintf("SELECT last(*) FROM %s"+ 114 " WHERE filter='%s' and address='%s' and blockchain='%s' and time>%d and time<%d and allExchanges=true group by time(1d) fill(previous) ORDER BY DESC", 115 table, filter, topAsset.Address, topAsset.Blockchain, starttime.UnixNano(), endtime.UnixNano()) 116 117 res, err := queryInfluxDB(datastore.influxClient, q) 118 if err != nil { 119 log.Errorln("GetFilterPoints", err) 120 } 121 log.Info("GetFilterPoints query: ", q, " returned ", len(res)) 122 123 if len(res) > 0 && len(res[0].Series) > 0 { 124 for i := 0; i < len(res[0].Series[0].Values); i++ { 125 126 var filterpoint dia.FilterPoint 127 128 filterpoint.Time, err = time.Parse(time.RFC3339, res[0].Series[0].Values[i][0].(string)) 129 if err != nil { 130 return allFilters, err 131 } 132 // if res[0].Series[0].Values[i][1] != nil { 133 // filterpoint.Exchange = res[0].Series[0].Values[i][1].(string) 134 // } 135 // if res[0].Series[0].Values[i][2] != nil { 136 // filterpoint.Name = res[0].Series[0].Values[i][2].(string) 137 // } 138 // if res[0].Series[0].Values[i][3] != nil { 139 // filterpoint.Asset.Symbol = res[0].Series[0].Values[i][3].(string) 140 // } 141 if res[0].Series[0].Values[i][2] != nil { 142 filterpoint.Value, err = res[0].Series[0].Values[i][2].(json.Number).Float64() 143 } else { 144 log.Errorln("res[0].Series[0].Values[i][2]", res[0].Series[0].Values[i][2]) 145 } 146 if err != nil { 147 return allFilters, err 148 } 149 allFilters = append(allFilters, filterpoint) 150 } 151 } else { 152 return allFilters, errors.New("no filter points in time range") 153 } 154 155 return allFilters, err 156 } 157 158 // GetFilterAllExchanges returns a slice of quotations for each exchange the asset given by 159 // @address and @blockchain has a filter value in the given time-range. 160 // It returns the most recent filter value in the given time-range for each exchange resp. 161 func (datastore *DB) GetFilterAllExchanges( 162 filter string, 163 address string, 164 blockchain string, 165 starttime time.Time, 166 endtime time.Time, 167 ) (assetQuotations []AssetQuotation, err error) { 168 q := fmt.Sprintf(` 169 SELECT time,address,blockchain,symbol,value 170 FROM %s 171 WHERE filter='%s' 172 AND allExchanges=false 173 AND address='%s' 174 AND blockchain='%s' 175 AND time>%d 176 AND time<=%d 177 GROUP BY "exchange" 178 ORDER BY DESC 179 LIMIT 1`, 180 influxDbFiltersTable, filter, address, blockchain, starttime.UnixNano(), endtime.UnixNano()) 181 182 res, err := queryInfluxDB(datastore.influxClient, q) 183 if err != nil { 184 log.Errorln("GetFilterPoints", err) 185 return 186 } 187 188 if len(res) > 0 && len(res[0].Series) > 0 { 189 for _, row := range res[0].Series { 190 var aq AssetQuotation 191 aq.Source = row.Tags["exchange"] 192 aq.Price, err = row.Values[0][4].(json.Number).Float64() 193 if err != nil { 194 log.Warn("parse price: ", err) 195 } 196 aq.Asset = dia.Asset{Address: address, Blockchain: blockchain, Symbol: row.Values[0][3].(string)} 197 aq.Time, err = time.Parse(time.RFC3339, row.Values[0][0].(string)) 198 if err != nil { 199 log.Warn("parse time: ", err) 200 } 201 assetQuotations = append(assetQuotations, aq) 202 } 203 } 204 205 return 206 } 207 208 func getKey(filter string, asset dia.Asset, exchange string) string { 209 key := filter + "_" + asset.Blockchain + "_" + asset.Address 210 if exchange != "" { 211 key = key + "_" + exchange 212 } 213 return key 214 } 215 216 func getKeyFilterZSET(key string) string { 217 return "dia_" + key + "_ZSET" 218 } 219 220 func getKeyFilterSymbolAndExchangeZSET(filter string, asset dia.Asset, exchange string) string { 221 if exchange == "" { 222 return "dia_" + filter + "_" + asset.Blockchain + "_" + asset.Address + "_ZSET" 223 } else { 224 return "dia_" + filter + "_" + asset.Blockchain + "_" + asset.Address + "_ZSET" 225 } 226 } 227 228 // SaveFilterInflux stores a filter point in influx. 229 func (datastore *DB) SaveFilterInflux(filter string, asset dia.Asset, exchange string, value float64, t time.Time) error { 230 // Create a point and add to batch 231 tags := map[string]string{ 232 "filter": filter, 233 "symbol": EscapeReplacer.Replace(asset.Symbol), 234 "address": EscapeReplacer.Replace(asset.Address), 235 "blockchain": asset.Blockchain, 236 "exchange": exchange, 237 } 238 fields := map[string]interface{}{ 239 "value": value, 240 "allExchanges": exchange == "", 241 } 242 pt, err := clientInfluxdb.NewPoint(influxDbFiltersTable, tags, fields, t) 243 if err != nil { 244 log.Errorln("new filter influx:", err) 245 } else { 246 datastore.addPoint(pt) 247 } 248 return err 249 } 250 251 func (datastore *DB) setZSETValue(key string, value float64, unixTime int64, maxWindow int64) error { 252 if datastore.redisClient == nil { 253 return nil 254 } 255 member := strconv.FormatFloat(value, 'f', -1, 64) + " " + strconv.FormatInt(unixTime, 10) 256 257 err := datastore.redisPipe.ZAdd(key, redis.Z{ 258 Score: float64(unixTime), 259 Member: member, 260 }).Err() 261 log.Debug("SetZSETValue ", key, member, unixTime) 262 if err != nil { 263 log.Errorf("Error: %v on SetZSETValue %v\n", err, key) 264 } 265 // purging old values 266 err = datastore.redisPipe.ZRemRangeByScore(key, "-inf", "("+strconv.FormatInt(unixTime-maxWindow, 10)).Err() 267 if err != nil { 268 log.Errorf("Error: %v on SetZSETValue %v\n", err, key) 269 } 270 if err = datastore.redisPipe.Expire(key, TimeOutRedis).Err(); err != nil { 271 log.Error(err) 272 } //TODO put two commands together ? 273 return err 274 } 275 276 func (datastore *DB) getZSETValue(key string, atUnixTime int64) (float64, error) { 277 278 result := 0.0 279 max := strconv.FormatInt(atUnixTime, 10) 280 vals, err := datastore.redisClient.ZRangeByScoreWithScores(key, redis.ZRangeBy{ 281 Min: "-inf", 282 Max: max, 283 }).Result() 284 log.Debug(key, "vals: %v on getZSETValue maxScore: %v", vals, max) 285 if err == nil { 286 if len(vals) > 0 { 287 _, err = fmt.Sscanf(vals[len(vals)-1].Member.(string), "%f", &result) 288 if err != nil { 289 log.Error(err) 290 } 291 log.Debugf("returned value: %v", result) 292 } else { 293 err = errors.New("getZSETValue no value found") 294 } 295 } 296 return result, err 297 } 298 299 func (datastore *DB) getZSETLastValue(key string) (float64, int64, error) { 300 value := 0.0 301 var unixTime int64 302 vals, err := datastore.redisClient.ZRange(key, -1, -1).Result() 303 log.Debug(key, "on getZSETLastValue:", vals) 304 if err == nil { 305 if len(vals) == 1 { 306 _, err = fmt.Sscanf(vals[0], "%f %d", &value, &unixTime) 307 if err != nil { 308 log.Error(err) 309 } 310 log.Debugf("returned value: %v", value) 311 } else { 312 err = errors.New("getZSETLastValue no value found") 313 log.Errorln("Error: on getZSETLastValue", err, key) 314 } 315 } 316 return value, unixTime, err 317 }