Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: price-feeder housekeeping #634

Merged
merged 3 commits into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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