| |
| """ |
| CryptoCompare API Client - Comprehensive crypto data with API key authentication |
| Official API: https://min-api.cryptocompare.com/ |
| """ |
|
|
| import httpx |
| import logging |
| import os |
| from typing import Dict, Any, List, Optional |
| from datetime import datetime |
|
|
| logger = logging.getLogger(__name__) |
|
|
| |
| CRYPTOCOMPARE_API_KEY = os.getenv("CRYPTOCOMPARE_KEY", "e79c8e6d4c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f") |
|
|
|
|
| class CryptoCompareClient: |
| """ |
| CryptoCompare API Client - Market data, news, social stats, and more |
| """ |
| |
| def __init__(self, api_key: str = CRYPTOCOMPARE_API_KEY): |
| self.base_url = "https://min-api.cryptocompare.com/data" |
| self.api_key = api_key |
| self.timeout = 15.0 |
| |
| def _get_headers(self) -> Dict[str, str]: |
| """Get request headers with API key""" |
| headers = { |
| "Content-Type": "application/json" |
| } |
| if self.api_key: |
| headers["authorization"] = f"Apikey {self.api_key}" |
| return headers |
| |
| async def get_price(self, symbols: List[str], currency: str = "USD") -> Dict[str, Any]: |
| """ |
| Get current prices for multiple symbols |
| |
| Args: |
| symbols: List of cryptocurrency symbols (e.g., ["BTC", "ETH"]) |
| currency: Target currency (default: USD) |
| |
| Returns: |
| Price data from CryptoCompare |
| """ |
| try: |
| async with httpx.AsyncClient(timeout=self.timeout) as client: |
| fsyms = ",".join([s.upper() for s in symbols]) |
| |
| response = await client.get( |
| f"{self.base_url}/pricemultifull", |
| params={ |
| "fsyms": fsyms, |
| "tsyms": currency |
| }, |
| headers=self._get_headers() |
| ) |
| response.raise_for_status() |
| data = response.json() |
| |
| result = { |
| "data": data.get("RAW", {}), |
| "display": data.get("DISPLAY", {}), |
| "source": "CryptoCompare", |
| "timestamp": datetime.utcnow().isoformat() |
| } |
| |
| logger.info(f"β
CryptoCompare: Fetched prices for {len(symbols)} symbols") |
| return result |
| |
| except httpx.HTTPStatusError as e: |
| logger.error(f"β CryptoCompare API HTTP error: {e.response.status_code}") |
| raise |
| except Exception as e: |
| logger.error(f"β CryptoCompare API failed: {e}") |
| raise |
| |
| async def get_ohlcv( |
| self, |
| symbol: str, |
| currency: str = "USD", |
| limit: int = 100, |
| aggregate: int = 1 |
| ) -> Dict[str, Any]: |
| """ |
| Get OHLCV (candlestick) data |
| |
| Args: |
| symbol: Cryptocurrency symbol (e.g., "BTC") |
| currency: Target currency (default: USD) |
| limit: Number of data points (max 2000) |
| aggregate: Data aggregation (e.g., 1 = 1 hour, 24 = 1 day) |
| |
| Returns: |
| OHLCV data |
| """ |
| try: |
| async with httpx.AsyncClient(timeout=self.timeout) as client: |
| response = await client.get( |
| f"{self.base_url}/v2/histohour", |
| params={ |
| "fsym": symbol.upper(), |
| "tsym": currency.upper(), |
| "limit": limit, |
| "aggregate": aggregate |
| }, |
| headers=self._get_headers() |
| ) |
| response.raise_for_status() |
| data = response.json() |
| |
| if data.get("Response") == "Success": |
| logger.info(f"β
CryptoCompare: Fetched OHLCV for {symbol}") |
| return { |
| "symbol": symbol.upper(), |
| "data": data.get("Data", {}).get("Data", []), |
| "source": "CryptoCompare", |
| "timestamp": datetime.utcnow().isoformat() |
| } |
| else: |
| raise Exception(f"CryptoCompare API error: {data.get('Message', 'Unknown error')}") |
| |
| except httpx.HTTPStatusError as e: |
| logger.error(f"β CryptoCompare OHLCV HTTP error: {e.response.status_code}") |
| raise |
| except Exception as e: |
| logger.error(f"β CryptoCompare OHLCV failed: {e}") |
| raise |
| |
| async def get_news(self, limit: int = 50) -> Dict[str, Any]: |
| """ |
| Get latest crypto news from CryptoCompare |
| |
| Args: |
| limit: Number of news articles (max 200) |
| |
| Returns: |
| News articles |
| """ |
| try: |
| async with httpx.AsyncClient(timeout=self.timeout) as client: |
| response = await client.get( |
| f"{self.base_url}/v2/news/", |
| params={ |
| "lang": "EN", |
| "limit": limit |
| }, |
| headers=self._get_headers() |
| ) |
| response.raise_for_status() |
| data = response.json() |
| |
| if data.get("Type") == 100: |
| articles = data.get("Data", []) |
| logger.info(f"β
CryptoCompare: Fetched {len(articles)} news articles") |
| return { |
| "articles": articles, |
| "count": len(articles), |
| "source": "CryptoCompare", |
| "timestamp": datetime.utcnow().isoformat() |
| } |
| else: |
| raise Exception(f"CryptoCompare news error: {data.get('Message', 'Unknown error')}") |
| |
| except httpx.HTTPStatusError as e: |
| logger.error(f"β CryptoCompare news HTTP error: {e.response.status_code}") |
| raise |
| except Exception as e: |
| logger.error(f"β CryptoCompare news failed: {e}") |
| raise |
| |
| async def get_social_stats(self, coin_id: int) -> Dict[str, Any]: |
| """ |
| Get social statistics for a cryptocurrency |
| |
| Args: |
| coin_id: CryptoCompare coin ID |
| |
| Returns: |
| Social stats (Twitter, Reddit, etc.) |
| """ |
| try: |
| async with httpx.AsyncClient(timeout=self.timeout) as client: |
| response = await client.get( |
| f"{self.base_url}/social/coin/latest", |
| params={"coinId": coin_id}, |
| headers=self._get_headers() |
| ) |
| response.raise_for_status() |
| data = response.json() |
| |
| if data.get("Response") == "Success": |
| logger.info(f"β
CryptoCompare: Fetched social stats for coin {coin_id}") |
| return { |
| "data": data.get("Data", {}), |
| "source": "CryptoCompare", |
| "timestamp": datetime.utcnow().isoformat() |
| } |
| else: |
| raise Exception(f"CryptoCompare social stats error: {data.get('Message', 'Unknown error')}") |
| |
| except httpx.HTTPStatusError as e: |
| logger.error(f"β CryptoCompare social stats HTTP error: {e.response.status_code}") |
| raise |
| except Exception as e: |
| logger.error(f"β CryptoCompare social stats failed: {e}") |
| raise |
| |
| async def get_top_exchanges_by_volume(self, symbol: str, currency: str = "USD", limit: int = 10) -> Dict[str, Any]: |
| """ |
| Get top exchanges by trading volume for a symbol |
| |
| Args: |
| symbol: Cryptocurrency symbol |
| currency: Target currency |
| limit: Number of exchanges to return |
| |
| Returns: |
| Top exchanges data |
| """ |
| try: |
| async with httpx.AsyncClient(timeout=self.timeout) as client: |
| response = await client.get( |
| f"{self.base_url}/top/exchanges/full", |
| params={ |
| "fsym": symbol.upper(), |
| "tsym": currency.upper(), |
| "limit": limit |
| }, |
| headers=self._get_headers() |
| ) |
| response.raise_for_status() |
| data = response.json() |
| |
| if data.get("Response") == "Success": |
| exchanges = data.get("Data", {}).get("Exchanges", []) |
| logger.info(f"β
CryptoCompare: Fetched top {len(exchanges)} exchanges for {symbol}") |
| return { |
| "exchanges": exchanges, |
| "symbol": symbol.upper(), |
| "source": "CryptoCompare", |
| "timestamp": datetime.utcnow().isoformat() |
| } |
| else: |
| raise Exception(f"CryptoCompare exchanges error: {data.get('Message', 'Unknown error')}") |
| |
| except httpx.HTTPStatusError as e: |
| logger.error(f"β CryptoCompare exchanges HTTP error: {e.response.status_code}") |
| raise |
| except Exception as e: |
| logger.error(f"β CryptoCompare exchanges failed: {e}") |
| raise |
|
|
|
|
| |
| cryptocompare_client = CryptoCompareClient() |
|
|
|
|
| |
| async def fetch_cryptocompare_price(symbols: List[str]) -> Dict[str, Any]: |
| """Get prices from CryptoCompare""" |
| return await cryptocompare_client.get_price(symbols) |
|
|
|
|
| async def fetch_cryptocompare_news(limit: int = 50) -> Dict[str, Any]: |
| """Get news from CryptoCompare""" |
| return await cryptocompare_client.get_news(limit) |
|
|
|
|
| async def fetch_cryptocompare_ohlcv(symbol: str, limit: int = 100) -> Dict[str, Any]: |
| """Get OHLCV data from CryptoCompare""" |
| return await cryptocompare_client.get_ohlcv(symbol, limit=limit) |
|
|
|
|
| __all__ = [ |
| "CryptoCompareClient", |
| "cryptocompare_client", |
| "fetch_cryptocompare_price", |
| "fetch_cryptocompare_news", |
| "fetch_cryptocompare_ohlcv" |
| ] |
|
|