|
|
@@ -2,6 +2,7 @@ package com.danielbohry.stocks.service.portfolio;
|
|
|
|
|
|
import com.danielbohry.stocks.domain.Portfolio;
|
|
|
import com.danielbohry.stocks.domain.PortfolioHistory;
|
|
|
+import com.danielbohry.stocks.exception.BadRequestException;
|
|
|
import com.danielbohry.stocks.repository.portfolio.PortfolioHistoryEntity;
|
|
|
import com.danielbohry.stocks.repository.portfolio.PortfolioHistoryRepository;
|
|
|
import com.danielbohry.stocks.service.ExchangeService;
|
|
|
@@ -14,12 +15,14 @@ import org.springframework.scheduling.annotation.Scheduled;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
+import java.time.Instant;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
|
|
|
import static java.math.BigDecimal.ONE;
|
|
|
import static java.math.RoundingMode.HALF_UP;
|
|
|
import static java.time.Instant.now;
|
|
|
+import static java.time.temporal.ChronoUnit.DAYS;
|
|
|
|
|
|
@Slf4j
|
|
|
@Service
|
|
|
@@ -38,21 +41,29 @@ public class PortfolioHistoryService {
|
|
|
@SchedulerLock(name = "SnapshotService_createSnapshots", lockAtLeastFor = "PT2M", lockAtMostFor = "PT10M")
|
|
|
public void createSnapshots() {
|
|
|
List<Portfolio> portfolios = service.getAllIds().stream()
|
|
|
- .map(portfolioId -> service.get(portfolioId, DEFAULT_CURRENCY))
|
|
|
- .toList();
|
|
|
+ .map(portfolioId -> service.get(portfolioId, DEFAULT_CURRENCY))
|
|
|
+ .toList();
|
|
|
|
|
|
saveSnapshots(portfolios);
|
|
|
}
|
|
|
|
|
|
- @Cacheable(value = "portfolioHistory", key = "#portfolioId + '-' + #currency")
|
|
|
- public List<PortfolioHistory> getSnapshots(String portfolioId, String currency) {
|
|
|
- List<PortfolioHistory> history = repository.findAllByPortfolioId(portfolioId).stream()
|
|
|
- .map(entity -> PortfolioHistory.builder()
|
|
|
- .portfolioId(entity.getPortfolioId())
|
|
|
- .stocks(encryptService.decryptStocks(entity.getEncryptedStocks()))
|
|
|
- .createdAt(entity.getCreatedAt())
|
|
|
- .build()
|
|
|
- ).toList();
|
|
|
+ @Cacheable(value = "portfolioHistory", key = "#portfolioId + '-' + #currency + '-' + #range")
|
|
|
+ public List<PortfolioHistory> getSnapshots(String portfolioId, String currency, String range) {
|
|
|
+ Instant end = now();
|
|
|
+ Instant start = switch (range) {
|
|
|
+ case "3m" -> end.minus(90, DAYS);
|
|
|
+ case "6m" -> end.minus(180, DAYS);
|
|
|
+ case "12m" -> end.minus(365, DAYS);
|
|
|
+ default -> throw new BadRequestException("Unsupported range: " + range);
|
|
|
+ };
|
|
|
+
|
|
|
+ List<PortfolioHistory> history = repository.findAllByPortfolioIdAndCreatedAtBetween(portfolioId, start, end).stream()
|
|
|
+ .map(entity -> PortfolioHistory.builder()
|
|
|
+ .portfolioId(entity.getPortfolioId())
|
|
|
+ .stocks(encryptService.decryptStocks(entity.getEncryptedStocks()))
|
|
|
+ .createdAt(entity.getCreatedAt())
|
|
|
+ .build()
|
|
|
+ ).toList();
|
|
|
|
|
|
ExchangeService.ExchangeRateResponse exchangeRate = exchangeService.getCurrentRate(currency);
|
|
|
Map<String, BigDecimal> rates = exchangeRate.getConversionRates();
|
|
|
@@ -61,15 +72,15 @@ public class PortfolioHistoryService {
|
|
|
history.forEach(h -> {
|
|
|
BigDecimal quoteRate = rates.getOrDefault(DEFAULT_CURRENCY, ONE);
|
|
|
BigDecimal convertedPriceTotalValue = h.getTotalValueInUSD()
|
|
|
- .divide(quoteRate, 2, HALF_UP)
|
|
|
- .multiply(targetRate);
|
|
|
+ .divide(quoteRate, 2, HALF_UP)
|
|
|
+ .multiply(targetRate);
|
|
|
|
|
|
h.setTotalValue(convertedPriceTotalValue);
|
|
|
|
|
|
h.getStocks().forEach(s -> {
|
|
|
BigDecimal convertedStockPrice = s.getPrice()
|
|
|
- .divide(quoteRate, 2, HALF_UP)
|
|
|
- .multiply(targetRate);
|
|
|
+ .divide(quoteRate, 2, HALF_UP)
|
|
|
+ .multiply(targetRate);
|
|
|
|
|
|
s.setPrice(convertedStockPrice);
|
|
|
s.setTotal(convertedStockPrice.multiply(BigDecimal.valueOf(s.getQuantity())));
|
|
|
@@ -89,17 +100,17 @@ public class PortfolioHistoryService {
|
|
|
if (portfolios.isEmpty()) return;
|
|
|
|
|
|
List<PortfolioHistoryEntity> snapshots = portfolios.stream()
|
|
|
- .filter(portfolio -> !portfolio.getStocks().isEmpty())
|
|
|
- .map(portfolio -> new PortfolioHistory(portfolio.getId(), portfolio.getTotalValue(), portfolio.getTotalAssets(), portfolio.getStocks(), now()))
|
|
|
- .map(portfolio -> {
|
|
|
- String encryptedStocks = encryptService.encryptStocks(portfolio.getStocks());
|
|
|
- return PortfolioHistoryEntity.builder()
|
|
|
- .portfolioId(portfolio.getPortfolioId())
|
|
|
- .encryptedStocks(encryptedStocks)
|
|
|
- .createdAt(now())
|
|
|
- .build();
|
|
|
- })
|
|
|
- .toList();
|
|
|
+ .filter(portfolio -> !portfolio.getStocks().isEmpty())
|
|
|
+ .map(portfolio -> new PortfolioHistory(portfolio.getId(), portfolio.getTotalValue(), portfolio.getTotalAssets(), portfolio.getStocks(), now()))
|
|
|
+ .map(portfolio -> {
|
|
|
+ String encryptedStocks = encryptService.encryptStocks(portfolio.getStocks());
|
|
|
+ return PortfolioHistoryEntity.builder()
|
|
|
+ .portfolioId(portfolio.getPortfolioId())
|
|
|
+ .encryptedStocks(encryptedStocks)
|
|
|
+ .createdAt(now())
|
|
|
+ .build();
|
|
|
+ })
|
|
|
+ .toList();
|
|
|
|
|
|
repository.saveAll(snapshots);
|
|
|
}
|