from fastapi import APIRouter, Query from typing import Optional from backend.services.ohlcv_service import OHLCVService from backend.services.market_summary_service import MarketSummaryService from backend.services.news_service import NewsService from backend.services.sentiment_service import SentimentService from backend.services.whale_alerts_service import WhaleAlertsService from backend.services.provider_status_service import ProviderStatusService router = APIRouter() # Initialize services ohlcv_service = OHLCVService() market_service = MarketSummaryService() news_service = NewsService() sentiment_service = SentimentService() whale_service = WhaleAlertsService() provider_service = ProviderStatusService() # ===== 1. OHLCV CANDLES ===== @router.get("/api/services/ohlcv/{symbol}") async def get_ohlcv_candles( symbol: str, timeframe: str = Query(default='1h', description="Timeframe: 1m, 5m, 15m, 1h, 4h, 1d"), limit: int = Query(default=100, ge=1, le=1000, description="Number of candles to return") ): """ Get OHLCV candles for a symbol. Returns: - status: 'available', 'unavailable', or 'degraded' - candles: List of candle objects with fields: - symbol: str - timeframe: str - timestamp: ISO datetime - open: float - high: float - low: float - close: float - volume: float """ return ohlcv_service.get_candles(symbol, timeframe, limit) @router.get("/api/services/ohlcv/{symbol}/latest") async def get_latest_candle( symbol: str, timeframe: str = Query(default='1h', description="Timeframe: 1m, 5m, 15m, 1h, 4h, 1d") ): """ Get the latest OHLCV candle for a symbol. Returns: - status: 'available', 'unavailable', or 'degraded' - candle: Single candle object with same fields as above """ return ohlcv_service.get_latest_candle(symbol, timeframe) # ===== 2. MARKET SUMMARY ===== @router.get("/api/services/market/{symbol}") async def get_market_summary(symbol: str): """ Get market summary for a symbol. Returns: - status: 'available', 'unavailable', or 'degraded' - data: Market summary object with fields: - symbol: str - price_usd: float - market_cap_usd: float or None - circulating_supply: float or None - total_volume_24h_usd: float or None - price_change_24h_pct: float or None - liquidity_score: float (0.0-1.0) """ return market_service.get_market_summary(symbol) # ===== 3. NEWS FEED ===== @router.get("/api/services/news") async def get_news_feed( limit: int = Query(default=20, ge=1, le=100, description="Number of news items"), source: Optional[str] = Query(default=None, description="Filter by source"), symbol: Optional[str] = Query(default=None, description="Filter by symbol") ): """ Get crypto news feed. Returns: - status: 'available', 'unavailable', or 'degraded' - count: int - news: List of news objects with fields: - id: str - title: str - summary: str or None - url: str - published_at: ISO datetime - source_name: str - sentiment_label: str ('positive', 'neutral', 'negative', or None) """ return news_service.get_news_feed(limit, source, symbol) # ===== 4. SENTIMENT ANALYSIS ===== @router.post("/api/services/sentiment/analyze") async def analyze_sentiment(payload: dict): """ Analyze sentiment of given text. Request body: { "text": "Bitcoin is performing very well today!" } Returns: - status: 'available', 'unavailable', or 'degraded' - sentiment: Object with fields: - label: str ('positive', 'neutral', or 'negative') - score: float (0.0-1.0, confidence score) - model_id: str (identifier of model used) """ text = payload.get('text', '') if not text: return { 'status': 'unavailable', 'error': 'No text provided', 'sentiment': None } return sentiment_service.analyze_sentiment(text) # ===== 5. WHALE / LARGE-TRANSFER ALERTS ===== @router.get("/api/services/whales") async def get_whale_alerts( min_amount_usd: float = Query(default=1000000, ge=0, description="Minimum transaction value in USD"), limit: int = Query(default=50, ge=1, le=100, description="Number of alerts"), blockchain: Optional[str] = Query(default=None, description="Filter by blockchain (ethereum, bsc, etc)") ): """ Get whale transaction alerts. Returns: - status: 'available', 'unavailable', or 'degraded' - count: int - alerts: List of whale transaction objects with fields: - txid: str - token_symbol: str - amount_usd: float - from_address: str - to_address: str - timestamp: ISO datetime - confidence_score: float (0.0-1.0) """ return whale_service.get_whale_alerts(min_amount_usd, limit, blockchain) # ===== 6. PROVIDER STATUS & LOGS ===== @router.get("/api/services/providers/status") async def get_provider_status(): """ Get provider counts and status. Returns: - status: 'available', 'unavailable', or 'degraded' - total_providers: int - available: int - degraded: int - offline: int - providers: Dict mapping provider_id to status """ return provider_service.get_provider_counts() @router.get("/api/services/providers/logs") async def get_provider_logs( limit: int = Query(default=100, ge=1, le=1000, description="Number of log events") ): """ Get last N JSONL log events. Returns: - status: 'available', 'unavailable', or 'degraded' - count: int - events: List of JSONL log event objects """ return provider_service.get_log_events(limit) @router.get("/api/services/providers/{provider_id}/health") async def get_provider_health(provider_id: str): """ Get health status for a specific provider. Returns: - status: 'available', 'unavailable', or 'degraded' - provider_id: str - last_check: ISO datetime - response_time_ms: float - success_rate: float (0.0-1.0) - rate_limit_remaining: int or None """ return provider_service.get_provider_health(provider_id) # ===== AVAILABILITY SUMMARY ===== @router.get("/api/services/availability") async def get_availability_summary(): """ Get availability summary for all services. Returns JSON with counts: - total: Total number of services - available: Number of available services - degraded: Number of degraded services - offline: Number of offline services - services: Dict mapping service_id to status """ services = { 'ohlcv': 'available', 'market_summary': 'available', 'news_feed': 'available', 'sentiment_analysis': 'available', 'whale_alerts': 'available', 'provider_status': 'available' } # Check actual service status try: # Test each service ohlcv_status = ohlcv_service.get_latest_candle('BTC', '1h') services['ohlcv'] = ohlcv_status.get('status', 'degraded') market_status = market_service.get_market_summary('BTC') services['market_summary'] = market_status.get('status', 'degraded') news_status = news_service.get_news_feed(1) services['news_feed'] = news_status.get('status', 'degraded') whale_status = whale_service.get_whale_alerts(1000000, 1) services['whale_alerts'] = whale_status.get('status', 'degraded') provider_status = provider_service.get_provider_counts() services['provider_status'] = provider_status.get('status', 'degraded') # Sentiment is always available (has fallback) services['sentiment_analysis'] = 'available' except Exception as e: pass # Keep default status on error total = len(services) available = sum(1 for status in services.values() if status == 'available') degraded = sum(1 for status in services.values() if status == 'degraded') offline = sum(1 for status in services.values() if status == 'offline') return { 'total': total, 'available': available, 'degraded': degraded, 'offline': offline, 'services': services }