| |
| """ |
| New Data Sources API Router |
| Exposes the newly integrated Crypto API Clean and Crypto DT Source APIs |
| """ |
|
|
| from fastapi import APIRouter, HTTPException, Query |
| from typing import Dict, Any, Optional, List |
| import logging |
|
|
| |
| from backend.services.crypto_api_clean_client import ( |
| get_crypto_api_clean_service, |
| CryptoAPICleanService |
| ) |
| from backend.services.crypto_dt_source_client import ( |
| get_crypto_dt_source_service, |
| CryptoDTSourceService |
| ) |
|
|
| |
| from backend.services.api_fallback_manager import get_fallback_manager |
|
|
| logger = logging.getLogger(__name__) |
|
|
| router = APIRouter(prefix="/api/new-sources", tags=["New Data Sources"]) |
|
|
| |
| crypto_api_clean = get_crypto_api_clean_service() |
| crypto_dt_source = get_crypto_dt_source_service() |
|
|
| |
| price_fallback = get_fallback_manager("cryptocurrency_prices") |
| sentiment_fallback = get_fallback_manager("sentiment_analysis") |
| resources_fallback = get_fallback_manager("resource_database") |
|
|
|
|
| |
|
|
| @router.get("/crypto-api-clean/health") |
| async def crypto_api_clean_health(): |
| """Health check for Crypto API Clean""" |
| result = await crypto_api_clean.health_check() |
| if not result["success"]: |
| raise HTTPException(status_code=503, detail=result.get("error", "Service unavailable")) |
| return result |
|
|
|
|
| @router.get("/crypto-api-clean/stats") |
| async def get_crypto_api_clean_stats(): |
| """ |
| Get statistics from Crypto API Clean resource database |
| Returns: Total resources (281+), categories (12), breakdown |
| """ |
| result = await crypto_api_clean.get_resources_stats() |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-api-clean/resources") |
| async def get_crypto_api_clean_resources( |
| category: Optional[str] = Query(None, description="Filter by category (e.g., market_data_apis, sentiment_apis)") |
| ): |
| """ |
| Get resources from Crypto API Clean database |
| |
| Categories available: |
| - rpc_nodes (24) |
| - block_explorers (33) |
| - market_data_apis (33) |
| - news_apis (17) |
| - sentiment_apis (14) |
| - onchain_analytics_apis (14) |
| - whale_tracking_apis (10) |
| - hf_resources (9) |
| - free_http_endpoints (13) |
| - cors_proxies (7) |
| - community_sentiment_apis (1) |
| - local_backend_routes (106) |
| """ |
| if category: |
| result = await crypto_api_clean.get_resources_by_category(category) |
| else: |
| result = await crypto_api_clean.get_all_resources() |
| |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-api-clean/categories") |
| async def get_crypto_api_clean_categories(): |
| """Get list of all resource categories""" |
| result = await crypto_api_clean.get_categories() |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| |
|
|
| @router.get("/crypto-dt-source/health") |
| async def crypto_dt_source_health(): |
| """Health check for Crypto DT Source""" |
| result = await crypto_dt_source.health_check() |
| if not result["success"]: |
| raise HTTPException(status_code=503, detail=result.get("error", "Service unavailable")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/status") |
| async def get_crypto_dt_source_status(): |
| """ |
| Get system status from Crypto DT Source |
| Returns: Models, datasets, external APIs availability |
| """ |
| result = await crypto_dt_source.get_status() |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/prices") |
| async def get_crypto_dt_prices( |
| ids: str = Query("bitcoin,ethereum", description="Comma-separated coin IDs"), |
| vs_currencies: str = Query("usd", description="Comma-separated currencies") |
| ): |
| """ |
| Get cryptocurrency prices from Crypto DT Source (via CoinGecko) |
| |
| Examples: |
| - /prices?ids=bitcoin&vs_currencies=usd |
| - /prices?ids=bitcoin,ethereum,solana&vs_currencies=usd,eur |
| """ |
| result = await crypto_dt_source.get_coingecko_price(ids=ids, vs_currencies=vs_currencies) |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/klines") |
| async def get_crypto_dt_klines( |
| symbol: str = Query("BTCUSDT", description="Trading pair"), |
| interval: str = Query("1h", description="Time interval (1m, 5m, 15m, 1h, 4h, 1d)"), |
| limit: int = Query(100, description="Number of candles", ge=1, le=1000) |
| ): |
| """ |
| Get candlestick/OHLCV data from Crypto DT Source (via Binance) |
| |
| Examples: |
| - /klines?symbol=BTCUSDT&interval=1h&limit=100 |
| - /klines?symbol=ETHUSDT&interval=4h&limit=50 |
| """ |
| result = await crypto_dt_source.get_binance_klines(symbol=symbol, interval=interval, limit=limit) |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/fear-greed") |
| async def get_crypto_dt_fear_greed( |
| limit: int = Query(1, description="Number of historical data points", ge=1, le=30) |
| ): |
| """ |
| Get Fear & Greed Index from Crypto DT Source (via Alternative.me) |
| |
| Returns index value (0-100) and classification: |
| - 0-24: Extreme Fear |
| - 25-49: Fear |
| - 50: Neutral |
| - 51-74: Greed |
| - 75-100: Extreme Greed |
| """ |
| result = await crypto_dt_source.get_fear_greed_index(limit=limit) |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/sentiment") |
| async def analyze_crypto_dt_sentiment( |
| text: str = Query(..., description="Text to analyze"), |
| model_key: str = Query("cryptobert_kk08", description="Model to use") |
| ): |
| """ |
| Analyze sentiment using Crypto DT Source HuggingFace models |
| |
| Available models: |
| - cryptobert_kk08: kk08/CryptoBERT |
| - twitter_sentiment: cardiffnlp/twitter-roberta-base-sentiment-latest |
| - finbert: ProsusAI/finbert |
| - cryptobert_elkulako: ElKulako/cryptobert |
| |
| Example: |
| - /sentiment?text=Bitcoin is doing great today!&model_key=cryptobert_kk08 |
| """ |
| result = await crypto_dt_source.get_hf_sentiment(text=text, model_key=model_key) |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/reddit") |
| async def get_crypto_dt_reddit( |
| subreddit: str = Query("cryptocurrency", description="Subreddit name"), |
| time_filter: str = Query("day", description="Time filter (hour, day, week, month, year, all)"), |
| limit: int = Query(25, description="Number of posts", ge=1, le=100) |
| ): |
| """ |
| Get top Reddit posts from Crypto DT Source |
| |
| Example: |
| - /reddit?subreddit=cryptocurrency&time_filter=day&limit=10 |
| - /reddit?subreddit=bitcoin&time_filter=week&limit=25 |
| """ |
| result = await crypto_dt_source.get_reddit_top(subreddit=subreddit, time_filter=time_filter, limit=limit) |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/news") |
| async def get_crypto_dt_news( |
| feed_name: str = Query("coindesk", description="Feed name (coindesk, cointelegraph, bitcoinmagazine, decrypt, theblock)"), |
| limit: int = Query(20, description="Number of articles", ge=1, le=100) |
| ): |
| """ |
| Get crypto news from RSS feeds via Crypto DT Source |
| |
| Available feeds: |
| - coindesk |
| - cointelegraph |
| - bitcoinmagazine |
| - decrypt |
| - theblock |
| |
| Example: |
| - /news?feed_name=coindesk&limit=10 |
| """ |
| result = await crypto_dt_source.get_rss_feed(feed_name=feed_name, limit=limit) |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/models") |
| async def get_crypto_dt_models(): |
| """ |
| Get list of available HuggingFace models in Crypto DT Source |
| |
| Returns 4 sentiment analysis models for crypto text analysis |
| """ |
| result = await crypto_dt_source.get_hf_models() |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| @router.get("/crypto-dt-source/datasets") |
| async def get_crypto_dt_datasets(): |
| """ |
| Get list of available HuggingFace datasets in Crypto DT Source |
| |
| Returns 5 crypto datasets including Bitcoin, Ethereum, Solana, and Ripple data |
| """ |
| result = await crypto_dt_source.get_hf_datasets() |
| if not result["success"]: |
| raise HTTPException(status_code=500, detail=result.get("error")) |
| return result |
|
|
|
|
| |
|
|
| @router.get("/prices/unified") |
| async def get_unified_prices( |
| ids: str = Query("bitcoin,ethereum", description="Comma-separated coin IDs") |
| ): |
| """ |
| Get cryptocurrency prices with automatic fallback |
| |
| Priority: |
| 1. Crypto DT Source (CoinGecko) |
| 2. Direct CoinGecko (if available) |
| 3. Other market data providers |
| """ |
| |
| async def fetch_from_crypto_dt(): |
| result = await crypto_dt_source.get_coingecko_price(ids=ids, vs_currencies="usd") |
| if result["success"]: |
| return result["data"] |
| raise Exception(result.get("error", "Unknown error")) |
| |
| |
| if not price_fallback.providers: |
| price_fallback.add_provider("CryptoDTSource", 1, fetch_from_crypto_dt, cooldown_seconds=180) |
| |
| result = await price_fallback.fetch_with_fallback() |
| |
| return { |
| "success": result["success"], |
| "data": result.get("data"), |
| "provider": result.get("provider"), |
| "attempts": result.get("attempts"), |
| "timestamp": result.get("timestamp") |
| } |
|
|
|
|
| @router.get("/resources/unified") |
| async def get_unified_resources( |
| category: Optional[str] = Query(None, description="Filter by category") |
| ): |
| """ |
| Get cryptocurrency resources with automatic fallback |
| |
| Priority: |
| 1. Crypto API Clean (281+ resources) |
| 2. Other resource databases (if available) |
| """ |
| |
| async def fetch_from_crypto_api_clean(): |
| if category: |
| result = await crypto_api_clean.get_resources_by_category(category) |
| else: |
| result = await crypto_api_clean.get_all_resources() |
| |
| if result["success"]: |
| return result["data"] |
| raise Exception(result.get("error", "Unknown error")) |
| |
| |
| if not resources_fallback.providers: |
| resources_fallback.add_provider("CryptoAPIClean", 1, fetch_from_crypto_api_clean, cooldown_seconds=180) |
| |
| result = await resources_fallback.fetch_with_fallback() |
| |
| return { |
| "success": result["success"], |
| "data": result.get("data"), |
| "provider": result.get("provider"), |
| "attempts": result.get("attempts") |
| } |
|
|
|
|
| |
|
|
| @router.get("/status") |
| async def get_new_sources_status(): |
| """ |
| Get comprehensive status of all new data sources |
| """ |
| crypto_api_clean_health = await crypto_api_clean.health_check() |
| crypto_dt_source_health = await crypto_dt_source.health_check() |
| |
| return { |
| "sources": { |
| "crypto_api_clean": { |
| "name": "Crypto API Clean", |
| "base_url": crypto_api_clean.base_url, |
| "status": "operational" if crypto_api_clean_health["success"] else "degraded", |
| "features": [ |
| "281+ cryptocurrency resources", |
| "12 resource categories", |
| "RPC nodes, block explorers, market data APIs", |
| "News APIs, sentiment APIs, on-chain analytics", |
| "Whale tracking, HuggingFace resources" |
| ], |
| "health": crypto_api_clean_health, |
| "priority": 2, |
| "weight": 75 |
| }, |
| "crypto_dt_source": { |
| "name": "Crypto DT Source", |
| "base_url": crypto_dt_source.base_url, |
| "status": "operational" if crypto_dt_source_health["success"] else "degraded", |
| "features": [ |
| "Unified cryptocurrency data API v2.0.0", |
| "4 HuggingFace sentiment models", |
| "5 crypto datasets", |
| "CoinGecko prices, Binance klines", |
| "Fear & Greed Index, Reddit posts, RSS feeds" |
| ], |
| "health": crypto_dt_source_health, |
| "priority": 2, |
| "weight": 75 |
| } |
| }, |
| "integration": { |
| "fallback_enabled": True, |
| "total_new_sources": 2, |
| "total_resources_added": "281+", |
| "integrated_date": "2025-12-13" |
| } |
| } |
|
|
|
|
| @router.get("/test-all") |
| async def test_all_new_sources(): |
| """ |
| Test all new data sources to verify integration |
| """ |
| results = {} |
| |
| |
| try: |
| stats = await crypto_api_clean.get_resources_stats() |
| results["crypto_api_clean"] = { |
| "status": "success" if stats["success"] else "failed", |
| "data": stats.get("data"), |
| "response_time_ms": stats.get("response_time_ms") |
| } |
| except Exception as e: |
| results["crypto_api_clean"] = { |
| "status": "error", |
| "error": str(e) |
| } |
| |
| |
| try: |
| status = await crypto_dt_source.get_status() |
| btc_price = await crypto_dt_source.get_btc_price() |
| results["crypto_dt_source"] = { |
| "status": "success" if status["success"] else "failed", |
| "system_status": status.get("data"), |
| "btc_price": btc_price, |
| "response_time_ms": status.get("response_time_ms") |
| } |
| except Exception as e: |
| results["crypto_dt_source"] = { |
| "status": "error", |
| "error": str(e) |
| } |
| |
| return { |
| "test_results": results, |
| "timestamp": "2025-12-13", |
| "all_tests_passed": all(r.get("status") == "success" for r in results.values()) |
| } |
|
|