Skip to content

Commit

Permalink
Merge pull request #36 from jmacxx/remove_stale_prices
Browse files Browse the repository at this point in the history
Remove stale prices that have not been updated for over 10 minutes.
  • Loading branch information
gabernard authored Oct 30, 2023
2 parents a4e1e18 + 2d00f25 commit fe73045
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 4 deletions.
6 changes: 5 additions & 1 deletion src/main/java/bisq/price/PriceProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public final T get() {
return cachedResult;
}

public final void put(T values) {
cachedResult = values;
}

@Override
public final void start() {
// do the initial refresh asynchronously
Expand Down Expand Up @@ -80,7 +84,7 @@ public void run() {
private void refresh() {
long ts = System.currentTimeMillis();

cachedResult = doGet();
put(doGet());

log.info("refresh took {} ms.", (System.currentTimeMillis() - ts));

Expand Down
18 changes: 18 additions & 0 deletions src/main/java/bisq/price/spot/ExchangeRateProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.math.RoundingMode;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -54,6 +55,7 @@
*/
public abstract class ExchangeRateProvider extends PriceProvider<Set<ExchangeRate>> {

private static final long STALE_PRICE_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis(10);
private static Set<String> SUPPORTED_CRYPTO_CURRENCIES = new HashSet<>();
private static Set<String> SUPPORTED_FIAT_CURRENCIES = new HashSet<>();
private final Set<String> providerExclusionList = new HashSet<>();
Expand Down Expand Up @@ -131,6 +133,22 @@ public String getPrefix() {
return prefix;
}

public void maybeClearStaleRates() {
// a stale rate is older than the specified interval, except:
// timestamp of 0L is used as special case re: CoinMarketCap and BitcoinAverage
// (https://github.com/bisq-network/bisq-pricenode/issues/23)
long staleTimestamp = new Date().getTime() - STALE_PRICE_INTERVAL_MILLIS;
Set<ExchangeRate> nonStaleRates = get().stream()
.filter(e -> e.getTimestamp() == 0L || e.getTimestamp() > staleTimestamp)
.collect(Collectors.toSet());
long numberOriginalRates = get().size();
if (numberOriginalRates > nonStaleRates.size()) {
put(nonStaleRates);
log.warn("{} {} stale rates removed, now {} rates",
getName(), numberOriginalRates, nonStaleRates.size());
}
}

@Override
protected void onRefresh() {
get().stream()
Expand Down
1 change: 1 addition & 0 deletions src/main/java/bisq/price/spot/ExchangeRateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public Map<String, Object> getAllMarketPrices() {
Map<String, ExchangeRate> aggregateExchangeRates = getAggregateExchangeRates();

providers.forEach(p -> {
p.maybeClearStaleRates();
// Specific metadata fields for specific providers are expected by the client,
// mostly for historical reasons
// Therefore, add metadata fields for all known providers
Expand Down
34 changes: 31 additions & 3 deletions src/test/java/bisq/price/spot/ExchangeRateServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,28 @@ public void getAllMarketPrices_withMultipleProviders_overlappingCurrencyCodes()
assertNotEquals(0L, retrievedData.get(providers.get(1).getPrefix() + "Ts"));
}

@Test
public void testStaleRatesRemoved() {
String fiatCoin = "BRL";
// insert a stale rate
long staleTimestamp = 10000L;
Long validTimestamp = System.currentTimeMillis();
List<ExchangeRateProvider> providers = asList(
buildDummyExchangeRateProviderWithRateAndTimestamp("mercadoBitcoin", fiatCoin, 129000.0, staleTimestamp),
buildDummyExchangeRateProviderWithRateAndTimestamp("coinGecko", fiatCoin, 129000.0, validTimestamp),
buildDummyExchangeRateProviderWithRateAndTimestamp("binance", fiatCoin, 131000.0, validTimestamp));
Map<String, Object> retrievedData = new ExchangeRateService(new StandardEnvironment(), providers, Collections.emptyList()).getAllMarketPrices();
doSanityChecksForRetrievedDataMultipleProviders(retrievedData, providers);
// check that the provider's stale rate was removed resulting in 0 rates and a 0 timestamp
assertEquals("0", retrievedData.get("mercadoBitcoinTs").toString());
assertEquals("0", retrievedData.get("mercadoBitcoinCount").toString());
// check that other providers are unaffected
assertEquals("1", retrievedData.get("coinGeckoCount").toString());
assertEquals("1", retrievedData.get("binanceCount").toString());
assertEquals(validTimestamp.toString(), retrievedData.get("coinGeckoTs").toString());
assertEquals(validTimestamp.toString(), retrievedData.get("binanceTs").toString());
}

@Test
public void bisqIndexCalculation_oneOutlierPriceWideRange() {
String fiatCoin = "BRL";
Expand Down Expand Up @@ -517,7 +539,13 @@ protected Set<ExchangeRate> doGet() {
return dummyProvider;
}

private ExchangeRateProvider buildDummyExchangeRateProviderWithRate(String providerName, String currencyCode, Double dummyRate) {
private ExchangeRateProvider buildDummyExchangeRateProviderWithRate(
String providerName, String currencyCode, Double rate) {
return buildDummyExchangeRateProviderWithRateAndTimestamp(
providerName, currencyCode, rate, System.currentTimeMillis());
}
private ExchangeRateProvider buildDummyExchangeRateProviderWithRateAndTimestamp(
String providerName, String currencyCode, Double rate, long timestamp) {
ExchangeRateProvider dummyProvider = new ExchangeRateProvider(
new StandardEnvironment(),
providerName,
Expand All @@ -534,8 +562,8 @@ protected Set<ExchangeRate> doGet() {
HashSet<ExchangeRate> exchangeRates = new HashSet<>();
exchangeRates.add(new ExchangeRate(
currencyCode,
dummyRate,
System.currentTimeMillis(),
rate,
timestamp,
getName())); // ExchangeRateProvider name
return exchangeRates;
}
Expand Down

0 comments on commit fe73045

Please sign in to comment.