Skip to content

Commit

Permalink
chore: price-feeder housekeeping (#634)
Browse files Browse the repository at this point in the history
(cherry picked from commit 2fdae2c)
  • Loading branch information
alexanderbez authored and mergify-bot committed Mar 9, 2022
1 parent ecc91df commit ecb0902
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 35 deletions.
64 changes: 38 additions & 26 deletions price-feeder/oracle/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ const (
tickerTimeout = 1000 * time.Millisecond
)

// deviationThreshold defines how many 𝜎 a provider can be away from the mean
// without being considered faulty.
var deviationThreshold = sdk.MustNewDecFromStr("2")
var (
// deviationThreshold defines how many 𝜎 a provider can be away from the mean
// without being considered faulty.
deviationThreshold = sdk.MustNewDecFromStr("2")
)

// PreviousPrevote defines a structure for defining the previous prevote
// submitted on-chain.
Expand Down Expand Up @@ -188,13 +190,13 @@ func (o *Oracle) SetPrices(ctx context.Context, acceptList oracletypes.DenomList
g.Go(func() error {
prices, err := priceProvider.GetTickerPrices(acceptedPairs...)
if err != nil {
telemetry.IncrCounter(1, "failure", "provider")
telemetry.IncrCounter(1, "failure", "provider", "type", "ticker")
return err
}

candles, err := priceProvider.GetCandlePrices(acceptedPairs...)
if err != nil {
telemetry.IncrCounter(1, "failure", "provider")
telemetry.IncrCounter(1, "failure", "provider", "type", "candle")
return err
}

Expand All @@ -221,11 +223,11 @@ func (o *Oracle) SetPrices(ctx context.Context, acceptList oracletypes.DenomList

if !pricesOk && !candlesOk {
mtx.Unlock()
return fmt.Errorf("failed to find any exchange rates in provider response")
return fmt.Errorf("failed to find any exchange rates in provider responses")
}
}
mtx.Unlock()

mtx.Unlock()
return nil
})
}
Expand All @@ -239,7 +241,7 @@ func (o *Oracle) SetPrices(ctx context.Context, acceptList oracletypes.DenomList
return err
}

// attempt to use candles for tvwap calculations
// attempt to use candles for TVWAP calculations
tvwapPrices, err := ComputeTVWAP(filteredCandles)
if err != nil {
return err
Expand Down Expand Up @@ -397,12 +399,12 @@ func (o *Oracle) filterTickerDeviations(
priceMap = make(map[string]map[string]sdk.Dec)
)

for providerName, providerPrices := range prices {
for providerName, priceTickers := range prices {
if _, ok := priceMap[providerName]; !ok {
priceMap[providerName] = make(map[string]sdk.Dec)
}
for base, price := range providerPrices {
priceMap[providerName][base] = price.Price
for base, tp := range priceTickers {
priceMap[providerName][base] = tp.Price
}
}

Expand All @@ -413,18 +415,22 @@ func (o *Oracle) filterTickerDeviations(

// accept any prices that are within 2𝜎, or for which we couldn't get 𝜎
for providerName, priceTickers := range prices {
for base, ticker := range priceTickers {
for base, tp := range priceTickers {
if _, ok := deviations[base]; !ok ||
(ticker.Price.GTE(means[base].Sub(deviations[base].Mul(deviationThreshold))) &&
ticker.Price.LTE(means[base].Add(deviations[base].Mul(deviationThreshold)))) {
(tp.Price.GTE(means[base].Sub(deviations[base].Mul(deviationThreshold))) &&
tp.Price.LTE(means[base].Add(deviations[base].Mul(deviationThreshold)))) {
if _, ok := filteredPrices[providerName]; !ok {
filteredPrices[providerName] = make(map[string]provider.TickerPrice)
}
filteredPrices[providerName][base] = ticker

filteredPrices[providerName][base] = tp
} else {
telemetry.IncrCounter(1, "failure", "provider")
o.logger.Warn().Str("base", base).Str("provider", providerName).Str(
"price", ticker.Price.String()).Msg("provider deviating from other prices")
telemetry.IncrCounter(1, "failure", "provider", "type", "ticker")
o.logger.Warn().
Str("base", base).
Str("provider", providerName).
Str("price", tp.Price.String()).
Msg("provider deviating from other prices")
}
}
}
Expand All @@ -442,26 +448,28 @@ func (o *Oracle) filterCandleDeviations(
tvwaps = make(map[string]map[string]sdk.Dec)
)

for providerName, c := range candles {
for providerName, priceCandles := range candles {
candlePrices := make(provider.AggregatedProviderCandles)

for assetName, asset := range c {
for base, cp := range priceCandles {
if _, ok := candlePrices[providerName]; !ok {
candlePrices[providerName] = make(map[string][]provider.CandlePrice)
}
candlePrices[providerName][assetName] = asset

candlePrices[providerName][base] = cp
}

tvwap, err := ComputeTVWAP(candlePrices)
if err != nil {
return nil, err
}

for assetName, asset := range tvwap {
for base, asset := range tvwap {
if _, ok := tvwaps[providerName]; !ok {
tvwaps[providerName] = make(map[string]sdk.Dec)
}
tvwaps[providerName][assetName] = asset

tvwaps[providerName][base] = asset
}
}

Expand All @@ -479,11 +487,15 @@ func (o *Oracle) filterCandleDeviations(
if _, ok := filteredCandles[providerName]; !ok {
filteredCandles[providerName] = make(map[string][]provider.CandlePrice)
}

filteredCandles[providerName][base] = candles[providerName][base]
} else {
telemetry.IncrCounter(1, "failure", "provider")
o.logger.Warn().Str("base", base).Str("provider", providerName).Str(
"price", price.String()).Msg("provider deviating from other candles")
telemetry.IncrCounter(1, "failure", "provider", "type", "candle")
o.logger.Warn().
Str("base", base).
Str("provider", providerName).
Str("price", price.String()).
Msg("provider deviating from other candles")
}
}
}
Expand Down
21 changes: 12 additions & 9 deletions price-feeder/oracle/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ import (
"github.com/umee-network/umee/price-feeder/oracle/provider"
)

var minimumTimeWeight = sdk.MustNewDecFromStr("0.2")
var (
minimumTimeWeight = sdk.MustNewDecFromStr("0.2")
)

// tvwapCandlePeriod represents the time period we use for tvwap in minutes.
const tvwapCandlePeriod = 3 * time.Minute
const (
// tvwapCandlePeriod represents the time period we use for tvwap in minutes
tvwapCandlePeriod = 3 * time.Minute
)

// compute VWAP for each base by dividing the Σ {P * V} by Σ {V}
func vwap(weightedPrices map[string]sdk.Dec, volumeSum map[string]sdk.Dec) (map[string]sdk.Dec, error) {
Expand Down Expand Up @@ -121,9 +125,8 @@ func ComputeTVWAP(prices provider.AggregatedProviderCandles) (map[string]sdk.Dec
// StandardDeviation returns maps of the standard deviations and means of assets.
// Will skip calculating for an asset if there are less than 3 prices.
func StandardDeviation(
prices map[string]map[string]sdk.Dec) (
map[string]sdk.Dec, map[string]sdk.Dec, error,
) {
prices map[string]map[string]sdk.Dec,
) (map[string]sdk.Dec, map[string]sdk.Dec, error) {
var (
deviations = make(map[string]sdk.Dec)
means = make(map[string]sdk.Dec)
Expand All @@ -132,16 +135,16 @@ func StandardDeviation(
)

for _, providerPrices := range prices {
for base, tp := range providerPrices {
for base, p := range providerPrices {
if _, ok := priceSums[base]; !ok {
priceSums[base] = sdk.ZeroDec()
}
if _, ok := priceSlice[base]; !ok {
priceSlice[base] = []sdk.Dec{}
}

priceSums[base] = priceSums[base].Add(tp)
priceSlice[base] = append(priceSlice[base], tp)
priceSums[base] = priceSums[base].Add(p)
priceSlice[base] = append(priceSlice[base], p)
}
}

Expand Down
1 change: 1 addition & 0 deletions price-feeder/oracle/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ func TestStandardDeviation(t *testing.T) {
require.NoError(t, err)
require.Len(t, deviation, len(tc.expected))
require.Len(t, mean, len(tc.expected))

for k, v := range tc.expected {
require.Equalf(t, v.deviation, deviation[k], "unexpected deviation for %s", k)
require.Equalf(t, v.mean, mean[k], "unexpected mean for %s", k)
Expand Down

0 comments on commit ecb0902

Please sign in to comment.