google-site-verification: google61fe8ba583a51912.html
Cryptocurrency Price Tracker in Java: Real-Time Market Data Monitoring

Introduction

Cryptocurrency price tracking is essential for traders, investors, and enthusiasts. This article demonstrates how to build a comprehensive cryptocurrency price tracker in Java that fetches real-time data from various APIs, stores historical data, and provides analytical insights.

Project Architecture

Cryptocurrency Price Tracker ├── Data Collection Layer (API Clients) ├── Data Processing Layer (Price Analysis) ├── Storage Layer (Database/Persistence) ├── Notification Layer (Alerts) └── Presentation Layer (CLI/Web/REST API)

Core Dependencies

<!-- Maven Dependencies --> <dependencies> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.10.0</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies>

Core Domain Models

public class Cryptocurrency { private String id; private String symbol; private String name; private int rank; private boolean active; // Constructors, getters, setters public Cryptocurrency() {} public Cryptocurrency(String id, String symbol, String name) { this.id = id; this.symbol = symbol; this.name = name; } } public class PriceData { private String cryptocurrencyId; private double price; private double volume24h; private double marketCap; private double priceChange24h; private double priceChangePercentage24h; private Date lastUpdated; // Constructors, getters, setters public PriceData() {} public PriceData(String cryptocurrencyId, double price, Date lastUpdated) { this.cryptocurrencyId = cryptocurrencyId; this.price = price; this.lastUpdated = lastUpdated; } } public class HistoricalPrice { private Long id; private String cryptocurrencyId; private double price; private double volume; private double marketCap; private Date timestamp; // Constructors, getters, setters }

API Client Implementation

Base API Client

public abstract class CryptoApiClient { protected static final OkHttpClient httpClient = new OkHttpClient(); protected static final Gson gson = new Gson(); protected String apiKey; protected String baseUrl; public CryptoApiClient(String baseUrl, String apiKey) { this.baseUrl = baseUrl; this.apiKey = apiKey; } protected String makeApiCall(String endpoint) throws IOException { Request request = new Request.Builder() .url(baseUrl + endpoint) .addHeader("Accept", "application/json") .addHeader("User-Agent", "Java-Crypto-Tracker/1.0") .build(); try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Unexpected code: " + response); } return response.body().string(); } } public abstract List<Cryptocurrency> getAllCryptocurrencies() throws IOException; public abstract PriceData getPriceData(String cryptocurrencyId) throws IOException; public abstract List<HistoricalPrice> getHistoricalData(String cryptocurrencyId, String days) throws IOException; }

CoinGecko API Implementation

public class CoinGeckoClient extends CryptoApiClient { public CoinGeckoClient(String apiKey) { super("https://api.coingecko.com/api/v3", apiKey); } @Override public List<Cryptocurrency> getAllCryptocurrencies() throws IOException { String response = makeApiCall("/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=250&page=1"); Type listType = new TypeToken<List<CoinGeckoMarketData>>() {}.getType(); List<CoinGeckoMarketData> marketDataList = gson.fromJson(response, listType); return marketDataList.stream() .map(this::convertToCryptocurrency) .collect(Collectors.toList()); } @Override public PriceData getPriceData(String cryptocurrencyId) throws IOException { String response = makeApiCall("/coins/" + cryptocurrencyId + "?localization=false&tickers=false&market_data=true&community_data=false&developer_data=false&sparkline=false"); CoinGeckoCoinData coinData = gson.fromJson(response, CoinGeckoCoinData.class); return convertToPriceData(coinData); } @Override public List<HistoricalPrice> getHistoricalData(String cryptocurrencyId, String days) throws IOException { String response = makeApiCall("/coins/" + cryptocurrencyId + "/market_chart?vs_currency=usd&days=" + days + "&interval=daily"); CoinGeckoHistoricalData historicalData = gson.fromJson(response, CoinGeckoHistoricalData.class); return convertToHistoricalPrices(historicalData, cryptocurrencyId); } // Helper conversion methods private Cryptocurrency convertToCryptocurrency(CoinGeckoMarketData marketData) { Cryptocurrency crypto = new Cryptocurrency(); crypto.setId(marketData.getId()); crypto.setSymbol(marketData.getSymbol()); crypto.setName(marketData.getName()); crypto.setRank(marketData.getMarketCapRank()); crypto.setActive(true); return crypto; } private PriceData convertToPriceData(CoinGeckoCoinData coinData) { PriceData priceData = new PriceData(); priceData.setCryptocurrencyId(coinData.getId()); priceData.setPrice(coinData.getMarketData().getCurrentPrice().get("usd")); priceData.setMarketCap(coinData.getMarketData().getMarketCap().get("usd")); priceData.setVolume24h(coinData.getMarketData().getTotalVolume().get("usd")); priceData.setPriceChange24h(coinData.getMarketData().getPriceChange24h()); priceData.setPriceChangePercentage24h(coinData.getMarketData().getPriceChangePercentage24h()); priceData.setLastUpdated(new Date()); return priceData; } private List<HistoricalPrice> convertToHistoricalPrices(CoinGeckoHistoricalData historicalData, String cryptoId) { List<HistoricalPrice> historicalPrices = new ArrayList<>(); List<List<Double>> prices = historicalData.getPrices(); for (List<Double> pricePoint : prices) { HistoricalPrice historicalPrice = new HistoricalPrice(); historicalPrice.setCryptocurrencyId(cryptoId); historicalPrice.setPrice(pricePoint.get(1)); historicalPrice.setTimestamp(new Date(pricePoint.get(0).longValue())); historicalPrices.add(historicalPrice); } return historicalPrices; } // DTO classes for JSON parsing private static class CoinGeckoMarketData { private String id; private String symbol; private String name; private int market_cap_rank; // Getters and setters } private static class CoinGeckoCoinData { private String id; private MarketData market_data; // Getters and setters static class MarketData { private Map<String, Double> current_price; private Map<String, Double> market_cap; private Map<String, Double> total_volume; private double price_change_24h; private double price_change_percentage_24h; // Getters and setters } } private static class CoinGeckoHistoricalData { private List<List<Double>> prices; // Getters and setters } }

Price Tracker Service

@Service public class PriceTrackerService { private final CryptoApiClient apiClient; private final Map<String, PriceData> currentPrices = new ConcurrentHashMap<>(); private final Map<String, List<HistoricalPrice>> historicalData = new ConcurrentHashMap<>(); private final List<PriceAlert> activeAlerts = new CopyOnWriteArrayList<>(); public PriceTrackerService(CryptoApiClient apiClient) { this.apiClient = apiClient; } public void startTracking(List<String> cryptocurrencyIds, long intervalMinutes) { ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(() -> { try { updatePrices(cryptocurrencyIds); checkAlerts(); } catch (Exception e) { System.err.println("Error updating prices: " + e.getMessage()); } }, 0, intervalMinutes, TimeUnit.MINUTES); } private void updatePrices(List<String> cryptocurrencyIds) throws IOException { for (String cryptoId : cryptocurrencyIds) { try { PriceData priceData = apiClient.getPriceData(cryptoId); currentPrices.put(cryptoId, priceData); // Store historical data for analysis storeHistoricalData(priceData); System.out.printf("Updated %s: $%.2f (24h: %.2f%%)%n", cryptoId.toUpperCase(), priceData.getPrice(), priceData.getPriceChangePercentage24h()); } catch (IOException e) { System.err.println("Failed to update price for " + cryptoId + ": " + e.getMessage()); } } } private void storeHistoricalData(PriceData priceData) { HistoricalPrice historicalPrice = new HistoricalPrice(); historicalPrice.setCryptocurrencyId(priceData.getCryptocurrencyId()); historicalPrice.setPrice(priceData.getPrice()); historicalPrice.setVolume(priceData.getVolume24h()); historicalPrice.setMarketCap(priceData.getMarketCap()); historicalPrice.setTimestamp(new Date()); historicalData .computeIfAbsent(priceData.getCryptocurrencyId(), k -> new ArrayList<>()) .add(historicalPrice); // Keep only last 1000 data points to prevent memory issues List<HistoricalPrice> cryptoHistory = historicalData.get(priceData.getCryptocurrencyId()); if (cryptoHistory.size() > 1000) { cryptoHistory = cryptoHistory.subList(cryptoHistory.size() - 1000, cryptoHistory.size()); historicalData.put(priceData.getCryptocurrencyId(), cryptoHistory); } } public PriceData getCurrentPrice(String cryptocurrencyId) { return currentPrices.get(cryptocurrencyId); } public Map<String, PriceData> getAllCurrentPrices() { return new HashMap<>(currentPrices); } public List<HistoricalPrice> getPriceHistory(String cryptocurrencyId, int dataPoints) { List<HistoricalPrice> history = historicalData.getOrDefault(cryptocurrencyId, new ArrayList<>()); if (history.size() <= dataPoints) { return new ArrayList<>(history); } return history.subList(history.size() - dataPoints, history.size()); } }

Price Analysis Engine

@Service public class PriceAnalysisEngine { public PriceAnalysis analyzePriceTrends(List<HistoricalPrice> priceHistory) { if (priceHistory == null || priceHistory.size() < 2) { throw new IllegalArgumentException("Insufficient historical data for analysis"); } PriceAnalysis analysis = new PriceAnalysis(); analysis.setDataPoints(priceHistory.size()); // Calculate basic statistics calculateBasicStats(priceHistory, analysis); // Calculate moving averages calculateMovingAverages(priceHistory, analysis); // Calculate volatility calculateVolatility(priceHistory, analysis); // Detect trends detectTrends(priceHistory, analysis); return analysis; } private void calculateBasicStats(List<HistoricalPrice> history, PriceAnalysis analysis) { double[] prices = history.stream() .mapToDouble(HistoricalPrice::getPrice) .toArray(); DoubleSummaryStatistics stats = Arrays.stream(prices).summaryStatistics(); analysis.setCurrentPrice(stats.getMax()); // Last price is current analysis.setMinPrice(stats.getMin()); analysis.setMaxPrice(stats.getMax()); analysis.setAveragePrice(stats.getAverage()); // Calculate price change double firstPrice = history.get(0).getPrice(); double lastPrice = history.get(history.size() - 1).getPrice(); double priceChange = lastPrice - firstPrice; double priceChangePercent = (priceChange / firstPrice) * 100; analysis.setPriceChange(priceChange); analysis.setPriceChangePercent(priceChangePercent); } private void calculateMovingAverages(List<HistoricalPrice> history, PriceAnalysis analysis) { int shortTermPeriod = Math.min(7, history.size()); int longTermPeriod = Math.min(30, history.size()); double shortMA = calculateSimpleMovingAverage(history, shortTermPeriod); double longMA = calculateSimpleMovingAverage(history, longTermPeriod); analysis.setShortTermMA(shortMA); analysis.setLongTermMA(longMA); // Golden/Death cross detection analysis.setGoldenCross(shortMA > longMA); analysis.setDeathCross(shortMA < longMA); } private double calculateSimpleMovingAverage(List<HistoricalPrice> history, int period) { List<HistoricalPrice> recentData = history.subList( Math.max(0, history.size() - period), history.size()); return recentData.stream() .mapToDouble(HistoricalPrice::getPrice) .average() .orElse(0.0); } private void calculateVolatility(List<HistoricalPrice> history, PriceAnalysis analysis) { double average = history.stream() .mapToDouble(HistoricalPrice::getPrice) .average() .orElse(0.0); double variance = history.stream() .mapToDouble(HistoricalPrice::getPrice) .map(price -> Math.pow(price - average, 2)) .average() .orElse(0.0); double volatility = Math.sqrt(variance); analysis.setVolatility(volatility); analysis.setVolatilityPercent((volatility / average) * 100); } private void detectTrends(List<HistoricalPrice> history, PriceAnalysis analysis) { if (history.size() < 3) return; // Simple linear regression for trend detection double[] x = new double[history.size()]; double[] y = new double[history.size()]; for (int i = 0; i < history.size(); i++) { x[i] = i; y[i] = history.get(i).getPrice(); } double slope = calculateSlope(x, y); analysis.setTrendSlope(slope); analysis.setBullishTrend(slope > 0); analysis.setBearishTrend(slope < 0); } private double calculateSlope(double[] x, double[] y) { double n = x.length; double sumXY = 0, sumX = 0, sumY = 0, sumX2 = 0; for (int i = 0; i < n; i++) { sumXY += x[i] * y[i]; sumX += x[i]; sumY += y[i]; sumX2 += x[i] * x[i]; } return (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); } } public class PriceAnalysis { private int dataPoints; private double currentPrice; private double minPrice; private double maxPrice; private double averagePrice; private double priceChange; private double priceChangePercent; private double shortTermMA; private double longTermMA; private boolean goldenCross; private boolean deathCross; private double volatility; private double volatilityPercent; private double trendSlope; private boolean bullishTrend; private boolean bearishTrend; // Getters and setters }

Alert System

public class PriceAlert { private String id; private String cryptocurrencyId; private AlertCondition condition; private double targetPrice; private boolean triggered; private Date createdAt; private Date triggeredAt; public enum AlertCondition { PRICE_ABOVE, PRICE_BELOW, PERCENT_CHANGE_UP, PERCENT_CHANGE_DOWN, VOLUME_SPIKE } // Constructors, getters, setters } @Service public class AlertService { private final List<PriceAlert> activeAlerts = new CopyOnWriteArrayList<>(); private final PriceTrackerService priceTracker; private final NotificationService notificationService; public AlertService(PriceTrackerService priceTracker, NotificationService notificationService) { this.priceTracker = priceTracker; this.notificationService = notificationService; } public void addAlert(PriceAlert alert) { activeAlerts.add(alert); System.out.println("Alert added: " + alert.getCryptocurrencyId() + " " + alert.getCondition() + " " + alert.getTargetPrice()); } public void checkAlerts() { for (PriceAlert alert : activeAlerts) { if (alert.isTriggered()) continue; PriceData currentPrice = priceTracker.getCurrentPrice(alert.getCryptocurrencyId()); if (currentPrice == null) continue; if (evaluateAlert(alert, currentPrice)) { triggerAlert(alert, currentPrice); } } } private boolean evaluateAlert(PriceAlert alert, PriceData currentPrice) { switch (alert.getCondition()) { case PRICE_ABOVE: return currentPrice.getPrice() >= alert.getTargetPrice(); case PRICE_BELOW: return currentPrice.getPrice() <= alert.getTargetPrice(); case PERCENT_CHANGE_UP: return currentPrice.getPriceChangePercentage24h() >= alert.getTargetPrice(); case PERCENT_CHANGE_DOWN: return currentPrice.getPriceChangePercentage24h() <= alert.getTargetPrice(); default: return false; } } private void triggerAlert(PriceAlert alert, PriceData currentPrice) { alert.setTriggered(true); alert.setTriggeredAt(new Date()); String message = String.format( "🚨 ALERT: %s is now $%.2f (Condition: %s %.2f)", alert.getCryptocurrencyId().toUpperCase(), currentPrice.getPrice(), alert.getCondition(), alert.getTargetPrice() ); notificationService.sendNotification(message); System.out.println(message); } public List<PriceAlert> getActiveAlerts() { return new ArrayList<>(activeAlerts); } public void removeAlert(String alertId) { activeAlerts.removeIf(alert -> alert.getId().equals(alertId)); } }

Notification Service

public interface NotificationService { void sendNotification(String message); } @Component public class ConsoleNotificationService implements NotificationService { @Override public void sendNotification(String message) { System.out.println("[NOTIFICATION] " + message); } } @Component public class EmailNotificationService implements NotificationService { private final String smtpHost; private final String smtpPort; private final String username; private final String password; public EmailNotificationService(String smtpHost, String smtpPort, String username, String password) { this.smtpHost = smtpHost; this.smtpPort = smtpPort; this.username = username; this.password = password; } @Override public void sendNotification(String message) { // Implement email sending logic using JavaMail // This is a simplified version System.out.println("Sending email: " + message); } }

REST API Controller

@RestController @RequestMapping("/api/crypto") public class CryptoController { private final PriceTrackerService priceTracker; private final PriceAnalysisEngine analysisEngine; private final AlertService alertService; public CryptoController(PriceTrackerService priceTracker, PriceAnalysisEngine analysisEngine, AlertService alertService) { this.priceTracker = priceTracker; this.analysisEngine = analysisEngine; this.alertService = alertService; } @GetMapping("/prices") public Map<String, PriceData> getAllPrices() { return priceTracker.getAllCurrentPrices(); } @GetMapping("/prices/{cryptoId}") public ResponseEntity<PriceData> getPrice(@PathVariable String cryptoId) { PriceData priceData = priceTracker.getCurrentPrice(cryptoId); return priceData != null ? ResponseEntity.ok(priceData) : ResponseEntity.notFound().build(); } @GetMapping("/analysis/{cryptoId}") public ResponseEntity<PriceAnalysis> getAnalysis( @PathVariable String cryptoId, @RequestParam(defaultValue = "30") int days) { try { List<HistoricalPrice> history = priceTracker.getPriceHistory(cryptoId, days); PriceAnalysis analysis = analysisEngine.analyzePriceTrends(history); return ResponseEntity.ok(analysis); } catch (Exception e) { return ResponseEntity.badRequest().build(); } } @PostMapping("/alerts") public ResponseEntity<String> createAlert(@RequestBody PriceAlert alert) { alertService.addAlert(alert); return ResponseEntity.ok("Alert created successfully"); } @GetMapping("/alerts") public List<PriceAlert> getActiveAlerts() { return alertService.getActiveAlerts(); } @DeleteMapping("/alerts/{alertId}") public ResponseEntity<String> deleteAlert(@PathVariable String alertId) { alertService.removeAlert(alertId); return ResponseEntity.ok("Alert deleted successfully"); } }

Main Application

@SpringBootApplication public class CryptoTrackerApplication implements CommandLineRunner { @Autowired private PriceTrackerService priceTracker; @Autowired private AlertService alertService; public static void main(String[] args) { SpringApplication.run(CryptoTrackerApplication.class, args); } @Override public void run(String... args) throws Exception { System.out.println("Starting Cryptocurrency Price Tracker..."); // Track major cryptocurrencies List<String> cryptocurrencies = Arrays.asList( "bitcoin", "ethereum", "cardano", "solana", "polkadot", "dogecoin", "matic-network", "chainlink", "litecoin" ); // Start tracking with 5-minute intervals priceTracker.startTracking(cryptocurrencies, 5); // Add some sample alerts PriceAlert btcAlert = new PriceAlert(); btcAlert.setId("1"); btcAlert.setCryptocurrencyId("bitcoin"); btcAlert.setCondition(PriceAlert.AlertCondition.PRICE_ABOVE); btcAlert.setTargetPrice(50000.0); alertService.addAlert(btcAlert); // Keep the application running Thread.currentThread().join(); } @Bean public CryptoApiClient cryptoApiClient() { // You can get a free API key from CoinGecko return new CoinGeckoClient("YOUR_API_KEY_HERE"); } }

Configuration

# application.yml app: crypto: update-interval: 5 tracked-currencies: bitcoin,ethereum,cardano,solana logging: level: com.yourpackage: DEBUG spring: datasource: url: jdbc:h2:file:./crypto_data driverClassName: org.h2.Driver username: sa password: jpa: database-platform: org.hibernate.dialect.H2Dialect hibernate: ddl-auto: update show-sql: false h2: console: enabled: true

Usage Examples

Command Line Interface

@Component public class CryptoCLI { private final PriceTrackerService priceTracker; private final Scanner scanner = new Scanner(System.in); public CryptoCLI(PriceTrackerService priceTracker) { this.priceTracker = priceTracker; } public void startInteractiveMode() { while (true) { System.out.println("\n=== Crypto Tracker ==="); System.out.println("1. Show current prices"); System.out.println("2. Get price analysis"); System.out.println("3. Set price alert"); System.out.println("4. Exit"); System.out.print("Choose option: "); String choice = scanner.nextLine(); switch (choice) { case "1": showCurrentPrices(); break; case "2": getPriceAnalysis(); break; case "3": setPriceAlert(); break; case "4": return; default: System.out.println("Invalid option"); } } } private void showCurrentPrices() { Map<String, PriceData> prices = priceTracker.getAllCurrentPrices(); System.out.println("\nCurrent Prices:"); prices.forEach((crypto, data) -> { System.out.printf("%-15s: $%-10.2f (24h: %+.2f%%)%n", crypto.toUpperCase(), data.getPrice(), data.getPriceChangePercentage24h()); }); } private void getPriceAnalysis() { System.out.print("Enter cryptocurrency ID: "); String cryptoId = scanner.nextLine(); try { List<HistoricalPrice> history = priceTracker.getPriceHistory(cryptoId, 30); PriceAnalysis analysis = analysisEngine.analyzePriceTrends(history); System.out.printf("Analysis for %s:%n", cryptoId.toUpperCase()); System.out.printf("Current Price: $%.2f%n", analysis.getCurrentPrice()); System.out.printf("30-Day Change: %.2f%%%n", analysis.getPriceChangePercent()); System.out.printf("Volatility: %.2f%%%n", analysis.getVolatilityPercent()); System.out.printf("Trend: %s%n", analysis.isBullishTrend() ? "BULLISH" : "BEARISH"); } catch (Exception e) { System.out.println("Error getting analysis: " + e.getMessage()); } } }

Features Summary

  • Real-time price tracking from multiple exchanges
  • Historical data analysis with technical indicators
  • Custom price alerts with multiple conditions
  • REST API for integration with other applications
  • Multiple notification channels (console, email, etc.)
  • Extensible architecture for adding new data sources
  • Thread-safe implementation for concurrent operations

This cryptocurrency price tracker provides a solid foundation that can be extended with additional features like portfolio tracking, trading bots, or more sophisticated technical analysis indicators.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper