ExchangeAggregateRate exchangeAggregateRate = new ExchangeAggregateRate(new Date(), blockNrETH, blockNrBTC); }); exchangeAggregateRate.addExchangeEntry(exchangeEntryRate); }); List<ExchangeCurrencyRate> allExchangeCurrencyRates = exchangeAggregateRate.getAllExchangeCurrencyRates(enabledCryptoCurrency); exchangeAggregateRate.addExchangeAggregateCurrencyRate(aggCurrencyRate); });
public List<ExchangeCurrencyRate> getAllExchangeCurrencyRates(CurrencyType currencyType) { List<ExchangeCurrencyRate> allExchangeCurrencyRates = new ArrayList<>(); getExchangeEntryRates().stream().forEach((exchangeEntryRate) -> { allExchangeCurrencyRates.addAll(exchangeEntryRate.getExchangeCurrencyRates() .stream() .filter((currencyRate) -> currencyRate.getCurrencyType() == currencyType) .collect(Collectors.toList()) ); }); return allExchangeCurrencyRates; }
public int compare(ExchangeAggregateRate rate1, ExchangeAggregateRate rate2) { long time1 = rate1.getCreationDate() != null ? rate1.getCreationDate().getTime() : 0; long time2 = rate2.getCreationDate() != null ? rate2.getCreationDate().getTime() : 0; long diff1 = Math.abs(time1 - timestamp.toEpochMilli()); long diff2 = Math.abs(time2 - timestamp.toEpochMilli()); return Long.compare(diff1, diff2); } }));
private ExchangeAggregateRate createExchangeAggregateRateWithMultipleExchangeEntry(ExchangeType... exchangeTypes) { ExchangeAggregateRate aggregateRate = createExchangeAggregateRate(); Arrays.asList(exchangeTypes).stream().forEach((type) -> { ExchangeEntryRate entryRate = createExchangeEntryRate(type); ExchangeCurrencyRate rateBTC = createExchangeCurrencyRate(CurrencyType.BTC); ExchangeCurrencyRate rateETH = createExchangeCurrencyRate(CurrencyType.ETH); entryRate.addCurrencyRate(rateBTC); entryRate.addCurrencyRate(rateETH); aggregateRate.addExchangeEntry(entryRate); }); ExchangeAggregateCurrencyRate aggregateCurrencyRateBTC = createExchangeAggregateCurrencyRate( CurrencyType.BTC, aggregateRate.getAllExchangeCurrencyRates(CurrencyType.BTC)); ExchangeAggregateCurrencyRate aggregateCurrencyRateETH = createExchangeAggregateCurrencyRate( CurrencyType.ETH, aggregateRate.getAllExchangeCurrencyRates(CurrencyType.ETH)); aggregateRate.addExchangeAggregateCurrencyRate(aggregateCurrencyRateBTC); aggregateRate.addExchangeAggregateCurrencyRate(aggregateCurrencyRateETH); aggregateRateRepository.save(aggregateRate); return aggregateRate; }
@Test public void testGetRate_ClosestRate_Available() throws RateNotFoundException { Instant now = Instant.now(); ExchangeAggregateRate r1 = new ExchangeAggregateRate(Date.from(now.minus(30, ChronoUnit.MINUTES)), null, null); ExchangeAggregateRate r2 = new ExchangeAggregateRate(Date.from(now.minus(5, ChronoUnit.MINUTES)), null, null); ExchangeAggregateRate r3 = new ExchangeAggregateRate(Date.from(now.plus(10, ChronoUnit.MINUTES)), null, null); r1.addExchangeAggregateCurrencyRate(new ExchangeAggregateCurrencyRate(CurrencyType.ETH, new BigDecimal("1.23456789"))); r1.addExchangeAggregateCurrencyRate(new ExchangeAggregateCurrencyRate(CurrencyType.BTC, new BigDecimal("0.123456789"))); r2.addExchangeAggregateCurrencyRate(new ExchangeAggregateCurrencyRate(CurrencyType.ETH, new BigDecimal("2.23456789"))); r2.addExchangeAggregateCurrencyRate(new ExchangeAggregateCurrencyRate(CurrencyType.BTC, new BigDecimal("1.123456789"))); r3.addExchangeAggregateCurrencyRate(new ExchangeAggregateCurrencyRate(CurrencyType.ETH, new BigDecimal("3.23456789"))); r3.addExchangeAggregateCurrencyRate(new ExchangeAggregateCurrencyRate(CurrencyType.BTC, new BigDecimal("2.123456789"))); when(exchangeAggregateRateRepository.findAllByCreationDateBetweenOrderByCreationDateDesc(any(), any())) .thenReturn(Arrays.asList(r1, r2, r3)); BigDecimal rate = ratesProviderService.getRate(CurrencyType.ETH, now); assertNotNull(rate); assertEquals(new BigDecimal("2.23456789"), rate); }
@Test public void testFindBlockNrBtcGreaterThan_HavingMultipleExchanges() { createExchangeAggregateRateWithMultipleExchangeEntry(ExchangeType.BITSTAMP, ExchangeType.KRAKEN); Optional<ExchangeAggregateRate> optionalAggregateRate = aggregateRateRepository .findFirstOptionalByBlockNrBtcLessThanEqualOrderByBlockNrBtcDesc(new Long(1)); assertTrue(optionalAggregateRate.filter((aggRate) -> aggRate.getExchangeEntryRates().stream() .anyMatch((exRate) -> exRate.getExchangeType() == ExchangeType.BITSTAMP) ).isPresent() ); assertTrue(optionalAggregateRate.filter((aggRate) -> aggRate.getExchangeEntryRates().stream() .anyMatch((exRate) -> exRate.getExchangeType() == ExchangeType.KRAKEN) ).isPresent() ); assertTrue(optionalAggregateRate.filter((aggRate) -> aggRate.getExchangeAggregateCurrencyRates().stream() .anyMatch((aggregateCurrencyRate) -> aggregateCurrencyRate.getCurrencyType() == CurrencyType.BTC) ).isPresent() ); assertTrue(optionalAggregateRate.filter((aggRate) -> aggRate.getExchangeAggregateCurrencyRates().stream() .anyMatch((aggregateCurrencyRate) -> aggregateCurrencyRate.getCurrencyType() == CurrencyType.ETH) ).isPresent() ); }
private ExchangeAggregateRate createExchangeAggregateRate(Date creationDate, Long blockNrEth, Long blockNrBtc) { return new ExchangeAggregateRate( creationDate, blockNrEth, blockNrBtc ); }
public BigDecimal getLatestFromDB(CurrencyType currencyType) throws RateNotFoundException { Optional<ExchangeAggregateRate> exchangeAggregateRate = exchangeAggregateRateRepository.findFirstOptionalByOrderByCreationDateDesc(); return exchangeAggregateRate.flatMap((aggregateRate) -> aggregateRate.getExchangeAggregateCurrencyRates(currencyType)) .map((aggCurrencyRate) -> aggCurrencyRate.getAggregateExchangeRate()) .orElseThrow(() -> new RateNotFoundException(String.format("No rate aggregation found for %s-%s.", ratesAppConfigHolder.getBaseFiatCurrency(), currencyType))); }
@Test public void testFindAllByOrderDesc() { createMultipleExchangeAggregateRate(); List<ExchangeAggregateRate> aggregateRates = aggregateRateRepository .findAllByOrderByCreationDate(); long count = aggregateRates.stream().count(); assertTrue(!aggregateRates.isEmpty()); assertTrue(aggregateRates.stream().findFirst().filter((aggRate) -> aggRate.getBlockNrBtc() == 1).isPresent()); assertTrue(aggregateRates.stream().skip(count - 1).findFirst().filter((aggRate) -> aggRate.getBlockNrBtc() == 3).isPresent()); }
private ExchangeAggregateRate createExchangeAggregateRate(Date creationDate) { return new ExchangeAggregateRate(creationDate, null, null); }
public BigDecimal getRate(CurrencyType currencyType, Instant timestamp) throws RateNotFoundException { // fetch rates between the startDate and endDate Instant startRange = calculateRangeStartDate(timestamp); Instant endRange = calculateRangeEndDate(timestamp); List<ExchangeAggregateRate> rates = exchangeAggregateRateRepository .findAllByCreationDateBetweenOrderByCreationDateDesc(Date.from(startRange), Date.from(endRange)); // get the closest rate to the specified timestamp Optional<ExchangeAggregateRate> closestRateToTimestamp = getClosestRateToTimestamp(timestamp, rates); return closestRateToTimestamp // obtaining the rate from the DB for the specified currency type .flatMap((aggregateRate) -> aggregateRate.getExchangeAggregateCurrencyRates(currencyType)) .map((aggCurrencyRate) -> aggCurrencyRate.getAggregateExchangeRate()) .map(Optional::of) // if it was not found, try to get from the historical provider .orElseGet(() -> getRateFromHistoricalProviderAndSaveToDB(currencyType, timestamp)) // if not found, then throw an exception .orElseThrow(() -> new RateNotFoundException( String.format("Rate for currency %s with timestamp %s was not found either in the DB " + "or fetching from the historical provider API.", currencyType, timestamp.getEpochSecond()))); }
@Test public void testFindBlockNrBtcGreaterThan_Equals() { createExchangeAggregateRateWithMultipleExchangeEntry(ExchangeType.BITSTAMP); Optional<ExchangeAggregateRate> optionalAggregateRate = aggregateRateRepository .findFirstOptionalByBlockNrBtcLessThanEqualOrderByBlockNrBtcDesc(new Long(1)); assertTrue(optionalAggregateRate.isPresent()); assertTrue(optionalAggregateRate.filter((aggRate) -> aggRate.getBlockNrBtc() == 1).isPresent()); }
protected Optional<BigDecimal> getRateFromHistoricalProviderAndSaveToDB(CurrencyType currencyType, Instant timestamp) { ExchangeEntryRate exchangeEntryRate = getRateFromHistoricalProviderFromAllSupportedCurrencies(timestamp); // create the aggregation object ExchangeAggregateRate aggregateRate = new ExchangeAggregateRate(Date.from(Instant.now()), blockNrProviderService.getCurrentBlockNrEthereum(), blockNrProviderService.getCurrentBlockNrBitcoin()); aggregateRate.addExchangeEntry(exchangeEntryRate); if (exchangeEntryRate.getExchangeCurrencyRates() != null && !exchangeEntryRate.getExchangeCurrencyRates().isEmpty()) { exchangeEntryRate.getExchangeCurrencyRates().stream() .filter(distinctByKey(ExchangeCurrencyRate::getCurrencyType)) .forEach((entry) -> { // get all "ExchangeCurrencyRate" objects of distinct crypto currencies List<ExchangeCurrencyRate> allExchangeCurrencyRates = aggregateRate.getAllExchangeCurrencyRates(entry.getCurrencyType()); // remove outliers and get the mean BigDecimal mean = aggregationService.removeOutliersAndGetMean(allExchangeCurrencyRates); // create a new aggregate currency ExchangeAggregateCurrencyRate aggCurrencyRate = new ExchangeAggregateCurrencyRate(entry.getCurrencyType(), mean); // add to the top-level "ExchangeAggregateRate" aggregateRate.addExchangeAggregateCurrencyRate(aggCurrencyRate); }); // save to DB exchangeAggregateRateRepository.save(aggregateRate); } return aggregateRate.getExchangeAggregateCurrencyRates(currencyType) .map((aggregateObj) -> aggregateObj.getAggregateExchangeRate()); }
@Test(expected = RateNotFoundException.class) public void testGetRate_ClosestRate_Nor_HistoricalDate_Are_Available() throws Exception { Instant now = Instant.now(); ExchangeAggregateRate r1 = new ExchangeAggregateRate(Date.from(now.minus(30, ChronoUnit.MINUTES)), null, null); ExchangeAggregateRate r2 = new ExchangeAggregateRate(Date.from(now.minus(5, ChronoUnit.MINUTES)), null, null); ExchangeAggregateRate r3 = new ExchangeAggregateRate(Date.from(now.plus(10, ChronoUnit.MINUTES)), null, null); when(exchangeAggregateRateRepository.findAllByCreationDateBetweenOrderByCreationDateDesc(any(), any())) .thenReturn(Arrays.asList(r1, r2, r3)); when(cryptoCompareClientService.getHistorical(CryptoCompareCurrency.ETH, Arrays.asList(CryptoCompareCurrency.USD), now.getEpochSecond())) .thenThrow(new Exception("Historical API not available.")); ratesProviderService.getRate(CurrencyType.ETH, now); }
private boolean checkIfExchangeIsPresent(List<ExchangeAggregateRate> rates, ExchangeType exchangeType) { return rates.stream().anyMatch((aggregateRate) -> { return aggregateRate.getExchangeEntryRates().stream().anyMatch((entry) -> { return entry.getExchangeType() == exchangeType; }); }); }
@Test public void testGetClosestRateToTimestamp_OneEntryOnly() { Instant now = Instant.now(); List<ExchangeAggregateRate> rates = Arrays.asList( createExchangeAggregateRate(Date.from(now.plusSeconds(1))) ); Optional<ExchangeAggregateRate> closestRate = ratesProviderService.getClosestRateToTimestamp(now, rates); assertTrue(closestRate.isPresent()); assertTrue(now.isBefore(closestRate.get().getCreationDate().toInstant())); }
@Test public void testFindBlockNrBtcByOrderDesc() { createMultipleExchangeAggregateRate(); Optional<ExchangeAggregateRate> optionalAggregateRate = aggregateRateRepository .findFirstOptionalByOrderByBlockNrBtcDesc(); assertTrue(optionalAggregateRate.isPresent()); assertTrue(optionalAggregateRate.filter((aggRate) -> aggRate.getBlockNrBtc() == 3).isPresent()); }
@Test public void testGetRate_ClosestRate_Not_Available() throws Exception { Instant now = Instant.now(); ExchangeAggregateRate r1 = new ExchangeAggregateRate(Date.from(now.minus(30, ChronoUnit.MINUTES)), null, null); ExchangeAggregateRate r2 = new ExchangeAggregateRate(Date.from(now.minus(5, ChronoUnit.MINUTES)), null, null); ExchangeAggregateRate r3 = new ExchangeAggregateRate(Date.from(now.plus(10, ChronoUnit.MINUTES)), null, null); when(exchangeAggregateRateRepository.findAllByCreationDateBetweenOrderByCreationDateDesc(any(), any())) .thenReturn(Arrays.asList(r1, r2, r3)); List<CryptoCompareConversionRateResponse> rateResponses = Arrays.asList( new CryptoCompareConversionRateResponse(CryptoCompareCurrency.USD, new BigDecimal("9.2345")) ); CryptoCompareConversionRates conversionRates = new CryptoCompareConversionRates(rateResponses); List<CryptoCompareResponse> responses = Arrays.asList(new CryptoCompareResponse(CryptoCompareCurrency.ETH, conversionRates)); CryptoCompareResponseDTO dto = new CryptoCompareResponseDTO(responses); when(cryptoCompareClientService.getHistorical(CryptoCompareCurrency.ETH, Arrays.asList(CryptoCompareCurrency.USD), now.getEpochSecond())) .thenReturn(dto); BigDecimal rate = ratesProviderService.getRate(CurrencyType.ETH, now); assertNotNull(rate); assertEquals(new BigDecimal("9.2345"), rate); }
private boolean checkIfCurrencyIsPresent(List<ExchangeAggregateRate> rates, CurrencyType currencyType) { return rates.stream().anyMatch((aggregateRate) -> { return aggregateRate.getExchangeEntryRates().stream().anyMatch((entry) -> { return entry.getExchangeCurrencyRates().stream().anyMatch((currency) -> { return currency.getCurrencyType() == currencyType; }); }); }); }
@Test public void testGetClosestRateToTimestamp_TwoEntriesWithSameDifferenceShouldGetTheHigherOne() { Instant now = Instant.now(); List<ExchangeAggregateRate> rates = Arrays.asList( createExchangeAggregateRate(Date.from(now.plusSeconds(1))), createExchangeAggregateRate(Date.from(now.minusSeconds(1))) ); Optional<ExchangeAggregateRate> closestRate = ratesProviderService.getClosestRateToTimestamp(now, rates); assertTrue(closestRate.isPresent()); assertTrue(now.isBefore(closestRate.get().getCreationDate().toInstant())); }