github.com/diadata-org/diadata@v1.4.593/pkg/model/pairs.go (about) 1 package models 2 3 import ( 4 "context" 5 "database/sql" 6 "errors" 7 "fmt" 8 "strings" 9 10 "github.com/diadata-org/diadata/pkg/dia" 11 "github.com/jackc/pgx/v4" 12 ) 13 14 // GetExchangePair returns the unique exchange pair given by @exchange and @foreignname from postgres. 15 // It also returns the underlying pair if existent. 16 // If @caseSensitive is false case of @foreignname is ignored. 17 func (rdb *RelDB) GetExchangePair(exchange string, foreignname string, caseSensitive bool) (dia.ExchangePair, error) { 18 var ( 19 exchangepair dia.ExchangePair 20 verified bool 21 query string 22 decimalsQuoteAsset sql.NullInt64 23 decimalsBaseAsset sql.NullInt64 24 ) 25 26 exchangepair.Exchange = exchange 27 28 if caseSensitive { 29 query = fmt.Sprintf(` 30 SELECT ep.symbol,ep.foreignname,ep.verified,a.symbol,a.name,a.address,a.blockchain,a.decimals,b.symbol,b.name,b.address,b.blockchain,b.decimals 31 FROM %s ep 32 INNER JOIN %s a 33 ON ep.id_quotetoken=a.asset_id 34 INNER JOIN %s b 35 ON ep.id_basetoken=b.asset_id 36 WHERE exchange=$1 AND foreignname=$2`, 37 exchangepairTable, 38 assetTable, 39 assetTable, 40 ) 41 } else { 42 query = fmt.Sprintf(` 43 SELECT ep.symbol,ep.foreignname,ep.verified,a.symbol,a.name,a.address,a.blockchain,a.decimals,b.symbol,b.name,b.address,b.blockchain,b.decimals 44 FROM %s ep 45 INNER JOIN %s a 46 ON ep.id_quotetoken=a.asset_id 47 INNER JOIN %s b 48 ON ep.id_basetoken=b.asset_id 49 WHERE exchange=$1 AND foreignname ILIKE $2`, 50 exchangepairTable, 51 assetTable, 52 assetTable, 53 ) 54 } 55 err := rdb.postgresClient.QueryRow(context.Background(), query, exchange, foreignname).Scan( 56 &exchangepair.Symbol, 57 &exchangepair.ForeignName, 58 &verified, 59 &exchangepair.UnderlyingPair.QuoteToken.Symbol, 60 &exchangepair.UnderlyingPair.QuoteToken.Name, 61 &exchangepair.UnderlyingPair.QuoteToken.Address, 62 &exchangepair.UnderlyingPair.QuoteToken.Blockchain, 63 &decimalsQuoteAsset, 64 &exchangepair.UnderlyingPair.BaseToken.Symbol, 65 &exchangepair.UnderlyingPair.BaseToken.Name, 66 &exchangepair.UnderlyingPair.BaseToken.Address, 67 &exchangepair.UnderlyingPair.BaseToken.Blockchain, 68 &decimalsBaseAsset, 69 ) 70 if err != nil { 71 return dia.ExchangePair{}, err 72 } 73 if decimalsQuoteAsset.Valid { 74 exchangepair.UnderlyingPair.QuoteToken.Decimals = uint8(decimalsQuoteAsset.Int64) 75 } 76 if decimalsQuoteAsset.Valid { 77 exchangepair.UnderlyingPair.BaseToken.Decimals = uint8(decimalsBaseAsset.Int64) 78 } 79 80 exchangepair.Verified = verified 81 return exchangepair, nil 82 } 83 84 // SetExchangePair adds @pair to exchangepair table. 85 // If cache==true, it is also cached into redis 86 func (rdb *RelDB) SetExchangePair(exchange string, pair dia.ExchangePair, cache bool) error { 87 var query string 88 query = fmt.Sprintf(` 89 INSERT INTO %s (symbol,foreignname,exchange) 90 SELECT $1,$2,$3 91 WHERE NOT EXISTS (SELECT 1 FROM %s WHERE symbol=$1 AND foreignname=$2 AND exchange=$3)`, 92 exchangepairTable, 93 exchangepairTable, 94 ) 95 _, err := rdb.postgresClient.Exec(context.Background(), query, pair.Symbol, pair.ForeignName, exchange) 96 if err != nil { 97 return err 98 } 99 basetokenID, err := rdb.GetAssetID(pair.UnderlyingPair.BaseToken) 100 if err != nil { 101 log.Error(err) 102 } 103 quotetokenID, err := rdb.GetAssetID(pair.UnderlyingPair.QuoteToken) 104 if err != nil { 105 log.Error(err) 106 } 107 if basetokenID != "" { 108 query = fmt.Sprintf("UPDATE %s SET id_basetoken='%s' WHERE foreignname='%s' AND exchange='%s'", exchangepairTable, basetokenID, pair.ForeignName, exchange) 109 _, err = rdb.postgresClient.Exec(context.Background(), query) 110 if err != nil { 111 return err 112 } 113 } 114 if quotetokenID != "" { 115 query = fmt.Sprintf("UPDATE %s SET id_quotetoken='%s' WHERE foreignname='%s' AND exchange='%s'", exchangepairTable, quotetokenID, pair.ForeignName, exchange) 116 _, err = rdb.postgresClient.Exec(context.Background(), query) 117 if err != nil { 118 return err 119 } 120 } 121 query = fmt.Sprintf("UPDATE %s SET verified='%v' WHERE foreignname='%s' AND exchange='%s'", exchangepairTable, pair.Verified, pair.ForeignName, exchange) 122 _, err = rdb.postgresClient.Exec(context.Background(), query) 123 if err != nil { 124 return err 125 } 126 if cache { 127 err = rdb.SetExchangePairCache(exchange, pair) 128 if err != nil { 129 log.Errorf("setting pair %s to redis for exchange %s: %v", pair.ForeignName, exchange, err) 130 } 131 } 132 return nil 133 } 134 135 // GetExchangePairSeparator returns the separator that is used as notation for an exchange pair. 136 // Examples: BTC-USDT, BTCUSDT, BTC/USDT. 137 func (rdb *RelDB) GetExchangePairSeparator(exchange string) (string, error) { 138 var ( 139 foreignname string 140 symbolQuoteAsset string 141 symbolBaseAsset string 142 ) 143 query := fmt.Sprintf(` 144 SELECT ep.symbol,a.symbol,ep.foreignname 145 FROM %s ep 146 INNER JOIN %s a 147 ON ep.id_basetoken=a.asset_id 148 WHERE exchange=$1 149 LIMIT 1`, 150 exchangepairTable, 151 assetTable, 152 ) 153 err := rdb.postgresClient.QueryRow(context.Background(), query, exchange).Scan(&symbolQuoteAsset, &symbolBaseAsset, &foreignname) 154 if err != nil { 155 return "", err 156 } 157 if len(strings.Split(foreignname, symbolQuoteAsset)) < 2 { 158 return "", errors.New("not enough data.") 159 } 160 if len(strings.Split(strings.Split(foreignname, symbolQuoteAsset)[1], symbolBaseAsset)) < 1 { 161 return "", errors.New("not enough data.") 162 } 163 separator := strings.Split(strings.Split(foreignname, symbolQuoteAsset)[1], symbolBaseAsset)[0] 164 return separator, nil 165 } 166 167 // GetExchangePairSymbols returns all foreign names on @exchange from exchangepair table. 168 func (rdb *RelDB) GetExchangePairSymbols(exchange string) (pairs []dia.ExchangePair, err error) { 169 query := fmt.Sprintf("SELECT symbol,foreignname FROM %s WHERE exchange=$1", exchangepairTable) 170 var rows pgx.Rows 171 rows, err = rdb.postgresClient.Query(context.Background(), query, exchange) 172 if err != nil { 173 return 174 } 175 defer rows.Close() 176 177 for rows.Next() { 178 pair := dia.ExchangePair{Exchange: exchange} 179 err = rows.Scan(&pair.Symbol, &pair.ForeignName) 180 if err != nil { 181 return 182 } 183 pairs = append(pairs, pair) 184 } 185 return 186 } 187 188 // GetExchangePairs returns all pairs on a (centralized) @exchange. 189 func (rdb *RelDB) GetPairsForExchange(exchange dia.Exchange, filterVerified bool, verified bool) ([]dia.ExchangePair, error) { 190 var ( 191 pairs []dia.ExchangePair 192 rows pgx.Rows 193 err error 194 ) 195 exchangeType := GetExchangeType(exchange) 196 if exchangeType != "CEX" { 197 err = errors.New("query only feasible for centralized exchanges.") 198 return pairs, err 199 } 200 201 query := fmt.Sprintf(` 202 SELECT a.symbol,a.name,a.address,a.blockchain,a.decimals,b.symbol,b.name,b.address,b.blockchain,b.decimals,e.verified,e.foreignname 203 FROM %s e 204 INNER JOIN %s a 205 ON e.id_quotetoken=a.asset_id 206 INNER JOIN %s b 207 ON e.id_basetoken=b.asset_id 208 WHERE e.exchange=$1`, 209 exchangepairTable, 210 assetTable, 211 assetTable, 212 ) 213 if filterVerified { 214 query += " AND e.verified=$2" 215 } 216 217 if filterVerified { 218 rows, err = rdb.postgresClient.Query(context.Background(), query, exchange.Name, verified) 219 } else { 220 rows, err = rdb.postgresClient.Query(context.Background(), query, exchange.Name) 221 } 222 if err != nil { 223 return pairs, err 224 } 225 defer rows.Close() 226 227 for rows.Next() { 228 var ( 229 pair dia.ExchangePair 230 quoteDecimals sql.NullInt64 231 baseDecimals sql.NullInt64 232 ) 233 234 err := rows.Scan( 235 &pair.UnderlyingPair.QuoteToken.Symbol, 236 &pair.UnderlyingPair.QuoteToken.Name, 237 &pair.UnderlyingPair.QuoteToken.Address, 238 &pair.UnderlyingPair.QuoteToken.Blockchain, 239 "eDecimals, 240 &pair.UnderlyingPair.BaseToken.Symbol, 241 &pair.UnderlyingPair.BaseToken.Name, 242 &pair.UnderlyingPair.BaseToken.Address, 243 &pair.UnderlyingPair.BaseToken.Blockchain, 244 &baseDecimals, 245 &pair.Verified, 246 &pair.ForeignName, 247 ) 248 if err != nil { 249 return pairs, err 250 } 251 if quoteDecimals.Valid { 252 pair.UnderlyingPair.QuoteToken.Decimals = uint8(quoteDecimals.Int64) 253 } 254 if baseDecimals.Valid { 255 pair.UnderlyingPair.BaseToken.Decimals = uint8(baseDecimals.Int64) 256 } 257 pair.Exchange = exchange.Name 258 pair.Symbol = pair.UnderlyingPair.QuoteToken.Symbol 259 260 pairs = append(pairs, pair) 261 } 262 263 return pairs, nil 264 } 265 266 func (rdb *RelDB) GetPairsForAsset(asset dia.Asset, filterVerified bool, verified bool) ([]dia.ExchangePair, error) { 267 var ( 268 pairs []dia.ExchangePair 269 rows pgx.Rows 270 err error 271 ) 272 273 query := fmt.Sprintf(` 274 SELECT a.symbol,a.name,a.address,a.blockchain,a.decimals,b.symbol,b.name,b.address,b.blockchain,b.decimals,e.verified,e.foreignname,e.exchange 275 FROM %s e 276 INNER JOIN %s a 277 ON e.id_quotetoken=a.asset_id 278 INNER JOIN %s b 279 ON e.id_basetoken=b.asset_id 280 WHERE ((a.address=$1 and a.blockchain=$2) OR (b.address=$3 and b.blockchain=$4))`, 281 exchangepairTable, 282 assetTable, 283 assetTable, 284 ) 285 if filterVerified { 286 query += " AND e.verified=$5" 287 } 288 if filterVerified { 289 rows, err = rdb.postgresClient.Query(context.Background(), query, asset.Address, asset.Blockchain, asset.Address, asset.Blockchain, verified) 290 } else { 291 rows, err = rdb.postgresClient.Query(context.Background(), query, asset.Address, asset.Blockchain, asset.Address, asset.Blockchain) 292 } 293 if err != nil { 294 return pairs, err 295 } 296 defer rows.Close() 297 298 for rows.Next() { 299 var ( 300 pair dia.ExchangePair 301 quoteDecimals sql.NullInt64 302 baseDecimals sql.NullInt64 303 ) 304 305 err := rows.Scan( 306 &pair.UnderlyingPair.QuoteToken.Symbol, 307 &pair.UnderlyingPair.QuoteToken.Name, 308 &pair.UnderlyingPair.QuoteToken.Address, 309 &pair.UnderlyingPair.QuoteToken.Blockchain, 310 "eDecimals, 311 &pair.UnderlyingPair.BaseToken.Symbol, 312 &pair.UnderlyingPair.BaseToken.Name, 313 &pair.UnderlyingPair.BaseToken.Address, 314 &pair.UnderlyingPair.BaseToken.Blockchain, 315 &baseDecimals, 316 &pair.Verified, 317 &pair.ForeignName, 318 &pair.Exchange, 319 ) 320 if err != nil { 321 return pairs, err 322 } 323 if quoteDecimals.Valid { 324 pair.UnderlyingPair.QuoteToken.Decimals = uint8(quoteDecimals.Int64) 325 } 326 if baseDecimals.Valid { 327 pair.UnderlyingPair.BaseToken.Decimals = uint8(baseDecimals.Int64) 328 } 329 pair.Symbol = pair.UnderlyingPair.QuoteToken.Symbol 330 331 pairs = append(pairs, pair) 332 } 333 334 return pairs, nil 335 } 336 337 // GetExchangepairsByAsset returns all exchangepairs on @exchange where @asset is quotetoken for 338 // @basetoken=false and basetoken otherwise. 339 func (rdb *RelDB) GetExchangepairsByAsset(asset dia.Asset, exchange string, basetoken bool) (exchangepairs []dia.ExchangePair, err error) { 340 var ( 341 secondExchangeQuery string 342 baseQuoteQuery string 343 ) 344 if exchange == dia.BinanceExchange { 345 secondExchangeQuery = fmt.Sprintf(" OR ep.exchange='%s' OR ep.exchange='%s' ", dia.Binance2Exchange, dia.Binance3Exchange) 346 } 347 if exchange == dia.BKEXExchange { 348 secondExchangeQuery = fmt.Sprintf(" OR ep.exchange='%s'", dia.BKEX2Exchange) 349 } 350 if basetoken { 351 baseQuoteQuery = " b.address=$2 AND b.blockchain=$3 " 352 } else { 353 baseQuoteQuery = " a.address=$2 AND a.blockchain=$3 " 354 } 355 query := fmt.Sprintf(` 356 SELECT a.symbol,a.name,a.address,a.blockchain,a.decimals,b.symbol,b.name,b.address,b.blockchain,b.decimals,ep.foreignname,ep.exchange 357 FROM %s ep 358 INNER JOIN %s a 359 ON ep.id_quotetoken=a.asset_id 360 INNER JOIN %s b 361 ON ep.id_basetoken=b.asset_id 362 WHERE (ep.exchange=$1 %s) 363 AND %s 364 `, 365 exchangepairTable, 366 assetTable, 367 assetTable, 368 secondExchangeQuery, 369 baseQuoteQuery, 370 ) 371 rows, err := rdb.postgresClient.Query(context.Background(), query, exchange, asset.Address, asset.Blockchain) 372 if err != nil { 373 return 374 } 375 defer rows.Close() 376 377 for rows.Next() { 378 var ( 379 pair dia.ExchangePair 380 quoteDecimals sql.NullInt64 381 baseDecimals sql.NullInt64 382 ) 383 384 err := rows.Scan( 385 &pair.UnderlyingPair.QuoteToken.Symbol, 386 &pair.UnderlyingPair.QuoteToken.Name, 387 &pair.UnderlyingPair.QuoteToken.Address, 388 &pair.UnderlyingPair.QuoteToken.Blockchain, 389 "eDecimals, 390 &pair.UnderlyingPair.BaseToken.Symbol, 391 &pair.UnderlyingPair.BaseToken.Name, 392 &pair.UnderlyingPair.BaseToken.Address, 393 &pair.UnderlyingPair.BaseToken.Blockchain, 394 &baseDecimals, 395 &pair.ForeignName, 396 &pair.Exchange, 397 ) 398 if err != nil { 399 return exchangepairs, err 400 } 401 if quoteDecimals.Valid { 402 pair.UnderlyingPair.QuoteToken.Decimals = uint8(quoteDecimals.Int64) 403 } 404 if baseDecimals.Valid { 405 pair.UnderlyingPair.BaseToken.Decimals = uint8(baseDecimals.Int64) 406 } 407 pair.Symbol = pair.UnderlyingPair.QuoteToken.Symbol 408 409 exchangepairs = append(exchangepairs, pair) 410 } 411 412 return exchangepairs, nil 413 } 414 415 // GetNumPairs returns the number of exchangepairs/pools on @exchange. 416 func (rdb *RelDB) GetNumPairs(exchange dia.Exchange) (numPairs int, err error) { 417 418 exchangeType := GetExchangeType(exchange) 419 switch exchangeType { 420 case "CEX": 421 pairs, err := rdb.GetExchangePairSymbols(exchange.Name) 422 if err != nil { 423 return len(pairs), err 424 } 425 numPairs = len(pairs) 426 case "DEX": 427 pools, err := rdb.GetAllPoolAddrsExchange(exchange.Name, float64(0)) 428 if err != nil { 429 return len(pools), err 430 } 431 numPairs = len(pools) 432 } 433 434 return 435 } 436 437 // GetAllExchangeAssets returns all assets traded as quotetoken on a CEX. 438 func (rdb *RelDB) GetAllExchangeAssets(verified bool) (assets []dia.Asset, err error) { 439 query := fmt.Sprintf(` 440 SELECT DISTINCT (a.address,a.blockchain), a.symbol,a.name,a.decimals FROM %s a 441 INNER JOIN %s ep 442 ON a.asset_id=ep.id_quotetoken 443 WHERE ep.verified=$1 444 `, 445 assetTable, 446 exchangepairTable, 447 ) 448 var rows pgx.Rows 449 rows, err = rdb.postgresClient.Query(context.Background(), query, verified) 450 if err != nil { 451 return 452 } 453 defer rows.Close() 454 455 for rows.Next() { 456 var ( 457 asset dia.Asset 458 tmp []interface{} 459 decimals sql.NullInt64 460 ) 461 462 err = rows.Scan(&tmp, &asset.Symbol, &asset.Name, &decimals) 463 if err != nil { 464 return 465 } 466 if decimals.Valid { 467 asset.Decimals = uint8(decimals.Int64) 468 } 469 if len(tmp) == 2 { 470 asset.Address = tmp[0].(string) 471 asset.Blockchain = tmp[1].(string) 472 } 473 assets = append(assets, asset) 474 } 475 return 476 }