@@ -24,6 +24,11 @@ import (
2424const (
2525 oraclePriceExpiration = time .Minute * 10
2626 oracleRecheckInterval = time .Minute * 3
27+
28+ // If the total USD volume of all oracles is less than
29+ // minimumUSDVolumeForOraclesAvg, the oracles will be ignored for
30+ // pricing averages.
31+ minimumUSDVolumeForOraclesAvg = 100_000
2732)
2833
2934// MarketReport contains a market's rates on various exchanges and the fiat
@@ -265,26 +270,36 @@ func fetchMarketPrice(ctx context.Context, baseID, quoteID uint32, log dex.Logge
265270 return 0 , nil , err
266271 }
267272
268- price , err := oracleAverage (oracles , log )
273+ price , usdVolume , err := oracleAverage (oracles , log )
274+ if err != nil {
275+ return 0 , nil , err
276+ }
277+ if usdVolume < minimumUSDVolumeForOraclesAvg {
278+ log .Meter ("oracle_low_volume_" + b .Symbol + "_" + q .Symbol , 12 * time .Hour ).Infof (
279+ "Rejecting oracle average price for %s. not enough volume (%.2f USD < %.2f)" ,
280+ b .Symbol + "_" + q .Symbol , usdVolume , float32 (minimumUSDVolumeForOraclesAvg ),
281+ )
282+ return 0 , oracles , nil
283+ }
269284 return price , oracles , err
270285}
271286
272- func oracleAverage (mkts []* OracleReport , log dex.Logger ) (float64 , error ) {
273- var weightedSum , usdVolume float64
287+ func oracleAverage (mkts []* OracleReport , log dex.Logger ) (rate , usdVolume float64 , _ error ) {
288+ var weightedSum float64
274289 var n int
275290 for _ , mkt := range mkts {
276291 n ++
277292 weightedSum += mkt .USDVol * (mkt .BestBuy + mkt .BestSell ) / 2
278293 usdVolume += mkt .USDVol
279294 }
280295 if usdVolume == 0 {
281- return 0 , nil // No markets have data. OK.
296+ return 0 , 0 , nil // No markets have data. OK.
282297 }
283298
284- rate : = weightedSum / usdVolume
299+ rate = weightedSum / usdVolume
285300 // TODO: Require a minimum USD volume?
286301 log .Tracef ("marketAveragedPrice: price calculated from %d markets: rate = %f, USD volume = %f" , n , rate , usdVolume )
287- return rate , nil
302+ return rate , usdVolume , nil
288303}
289304
290305func getRates (ctx context.Context , url string , thing any ) (err error ) {
@@ -325,7 +340,7 @@ func spread(ctx context.Context, addr string, baseSymbol, quoteSymbol string, lo
325340 }
326341 sell , buy , err = s (ctx , baseSymbol , quoteSymbol , log )
327342 if err != nil {
328- log .Errorf ("Error getting spread from %q: %v" , addr , err )
343+ log .Meter ( "spread_" + addr , time . Hour * 12 ). Errorf ("Error getting spread from %q: %v" , addr , err )
329344 return 0 , 0
330345 }
331346 return sell , buy
@@ -351,7 +366,8 @@ func oracleMarketReport(ctx context.Context, b, q *fiatrates.CoinpaprikaAsset, l
351366 QuoteCurrencyID string `json:"quote_currency_id"`
352367 MarketURL string `json:"market_url"`
353368 LastUpdated time.Time `json:"last_updated"`
354- TrustScore string `json:"trust_score"`
369+ TrustScore string `json:"trust_score"` // TrustScore appears to be deprecated?
370+ Outlier bool `json:"outlier"`
355371 Quotes map [string ]* coinpapQuote `json:"quotes"`
356372 }
357373
@@ -375,7 +391,7 @@ func oracleMarketReport(ctx context.Context, b, q *fiatrates.CoinpaprikaAsset, l
375391
376392 // Create filter for desirable matches.
377393 marketMatches := func (mkt * coinpapMarket ) bool {
378- if mkt .TrustScore != "high" {
394+ if mkt .TrustScore != "high" || mkt . Outlier {
379395 return false
380396 }
381397
@@ -441,35 +457,53 @@ func oracleMarketReport(ctx context.Context, b, q *fiatrates.CoinpaprikaAsset, l
441457type Spreader func (ctx context.Context , baseSymbol , quoteSymbol string , log dex.Logger ) (sell , buy float64 , err error )
442458
443459var spreaders = map [string ]Spreader {
444- "binance.com" : fetchBinanceSpread ,
460+ "binance.com" : fetchBinanceGlobalSpread ,
461+ "binance.us" : fetchBinanceUSSpread ,
445462 "coinbase.com" : fetchCoinbaseSpread ,
446463 "bittrex.com" : fetchBittrexSpread ,
447464 "hitbtc.com" : fetchHitBTCSpread ,
448465 "exmo.com" : fetchEXMOSpread ,
449466}
450467
451- var binanceGlobalIs451 atomic.Bool
468+ var binanceGlobalIs451 , binanceUSIs451 atomic.Bool
469+
470+ func fetchBinanceGlobalSpread (ctx context.Context , baseSymbol , quoteSymbol string , log dex.Logger ) (sell , buy float64 , err error ) {
471+ if binanceGlobalIs451 .Load () {
472+ return 0 , 0 , nil
473+ }
474+ return fetchBinanceSpread (ctx , baseSymbol , quoteSymbol , false , log )
475+ }
452476
453- func fetchBinanceSpread (ctx context.Context , baseSymbol , quoteSymbol string , log dex.Logger ) (sell , buy float64 , err error ) {
477+ func fetchBinanceUSSpread (ctx context.Context , baseSymbol , quoteSymbol string , log dex.Logger ) (sell , buy float64 , err error ) {
478+ if binanceUSIs451 .Load () {
479+ return 0 , 0 , nil
480+ }
481+ return fetchBinanceSpread (ctx , baseSymbol , quoteSymbol , true , log )
482+ }
483+
484+ func fetchBinanceSpread (ctx context.Context , baseSymbol , quoteSymbol string , isUS bool , log dex.Logger ) (sell , buy float64 , err error ) {
454485 slug := fmt .Sprintf ("%s%s" , strings .ToUpper (baseSymbol ), strings .ToUpper (quoteSymbol ))
455486 var url string
456- var isGlobal bool
457- if binanceGlobalIs451 .Load () {
487+ if isUS {
458488 url = fmt .Sprintf ("https://api.binance.us/api/v3/ticker/bookTicker?symbol=%s" , slug )
459489 } else {
460- isGlobal = true
461490 url = fmt .Sprintf ("https://api.binance.com/api/v3/ticker/bookTicker?symbol=%s" , slug )
462491 }
463492
464493 var resp struct {
465494 BidPrice float64 `json:"bidPrice,string"`
466495 AskPrice float64 `json:"askPrice,string"`
467496 }
497+
468498 code , err := getHTTPWithCode (ctx , url , & resp )
469499 if err != nil {
470- if isGlobal && code == http .StatusUnavailableForLegalReasons && binanceGlobalIs451 .CompareAndSwap (false , true ) {
471- log .Info ("Binance Global responded with a 451. Oracle will use Binance U.S." )
472- return fetchBinanceSpread (ctx , baseSymbol , quoteSymbol , log )
500+ if code == http .StatusUnavailableForLegalReasons {
501+ if isUS && binanceUSIs451 .CompareAndSwap (false , true ) {
502+ log .Debugf ("Binance U.S. responded with a 451. Disabling" )
503+ } else if ! isUS && binanceGlobalIs451 .CompareAndSwap (false , true ) {
504+ log .Debugf ("Binance Global responded with a 451. Disabling" )
505+ }
506+ return 0 , 0 , nil
473507 }
474508 return 0 , 0 , err
475509 }
@@ -554,7 +588,3 @@ func fetchEXMOSpread(ctx context.Context, baseSymbol, quoteSymbol string, _ dex.
554588
555589 return mkt .AskTop , mkt .BidTop , nil
556590}
557-
558- func shortSymbol (symbol string ) string {
559- return strings .Split (symbol , "." )[0 ]
560- }
0 commit comments