|
|
|
|
|
"""
|
|
|
Real Data API Router - ZERO MOCK DATA
|
|
|
All endpoints return REAL data from external APIs
|
|
|
"""
|
|
|
|
|
|
from fastapi import APIRouter, HTTPException, Query, Body, WebSocket, WebSocketDisconnect
|
|
|
from fastapi.responses import JSONResponse
|
|
|
from typing import Optional, List, Dict, Any
|
|
|
from datetime import datetime
|
|
|
from pydantic import BaseModel
|
|
|
import logging
|
|
|
import json
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
from backend.services.real_api_clients import (
|
|
|
cmc_client,
|
|
|
news_client,
|
|
|
blockchain_client,
|
|
|
hf_client
|
|
|
)
|
|
|
from backend.services.real_ai_models import ai_registry
|
|
|
from backend.services.real_websocket import ws_manager
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
router = APIRouter(tags=["Real Data API - NO MOCKS"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PredictRequest(BaseModel):
|
|
|
"""Model prediction request"""
|
|
|
symbol: str
|
|
|
context: Optional[str] = None
|
|
|
params: Optional[Dict[str, Any]] = None
|
|
|
|
|
|
|
|
|
class SentimentRequest(BaseModel):
|
|
|
"""Sentiment analysis request"""
|
|
|
text: str
|
|
|
mode: Optional[str] = "crypto"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.websocket("/ws")
|
|
|
async def websocket_endpoint(websocket: WebSocket):
|
|
|
"""
|
|
|
WebSocket endpoint for REAL-TIME updates
|
|
|
Broadcasts REAL data only - NO MOCK DATA
|
|
|
"""
|
|
|
client_id = str(uuid.uuid4())
|
|
|
|
|
|
try:
|
|
|
await ws_manager.connect(websocket, client_id)
|
|
|
|
|
|
|
|
|
while True:
|
|
|
data = await websocket.receive_text()
|
|
|
message = json.loads(data)
|
|
|
|
|
|
action = message.get("action")
|
|
|
|
|
|
if action == "subscribe":
|
|
|
channels = message.get("channels", [])
|
|
|
await ws_manager.subscribe(client_id, channels)
|
|
|
|
|
|
|
|
|
await ws_manager.send_personal_message(
|
|
|
{
|
|
|
"type": "subscribed",
|
|
|
"channels": channels,
|
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
|
},
|
|
|
client_id
|
|
|
)
|
|
|
|
|
|
elif action == "unsubscribe":
|
|
|
|
|
|
pass
|
|
|
|
|
|
elif action == "ping":
|
|
|
|
|
|
await ws_manager.send_personal_message(
|
|
|
{
|
|
|
"type": "pong",
|
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
|
},
|
|
|
client_id
|
|
|
)
|
|
|
|
|
|
except WebSocketDisconnect:
|
|
|
await ws_manager.disconnect(client_id)
|
|
|
logger.info(f"WebSocket client {client_id} disconnected normally")
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ WebSocket error for client {client_id}: {e}")
|
|
|
await ws_manager.disconnect(client_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/api/market")
|
|
|
async def get_market_snapshot():
|
|
|
"""
|
|
|
Get REAL market snapshot from CoinMarketCap
|
|
|
Priority: HF Space → CoinMarketCap → Error (NO MOCK DATA)
|
|
|
"""
|
|
|
try:
|
|
|
|
|
|
try:
|
|
|
hf_data = await hf_client.get_market_data()
|
|
|
if hf_data.get("success"):
|
|
|
logger.info("✅ Market data from HF Space")
|
|
|
return hf_data
|
|
|
except Exception as hf_error:
|
|
|
logger.warning(f"HF Space unavailable: {hf_error}")
|
|
|
|
|
|
|
|
|
cmc_data = await cmc_client.get_latest_listings(limit=50)
|
|
|
|
|
|
|
|
|
items = []
|
|
|
for coin in cmc_data["data"]:
|
|
|
quote = coin.get("quote", {}).get("USD", {})
|
|
|
items.append({
|
|
|
"symbol": coin["symbol"],
|
|
|
"name": coin["name"],
|
|
|
"price": quote.get("price", 0),
|
|
|
"change_24h": quote.get("percent_change_24h", 0),
|
|
|
"volume_24h": quote.get("volume_24h", 0),
|
|
|
"market_cap": quote.get("market_cap", 0),
|
|
|
"source": "coinmarketcap"
|
|
|
})
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"last_updated": datetime.utcnow().isoformat(),
|
|
|
"items": items,
|
|
|
"meta": {
|
|
|
"cache_ttl_seconds": 30,
|
|
|
"generated_at": datetime.utcnow().isoformat(),
|
|
|
"source": "coinmarketcap"
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ All market data sources failed: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real market data. All sources failed: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.get("/api/market/pairs")
|
|
|
async def get_trading_pairs():
|
|
|
"""
|
|
|
Get REAL trading pairs
|
|
|
Priority: HF Space → CoinMarketCap top pairs → Error
|
|
|
"""
|
|
|
try:
|
|
|
|
|
|
try:
|
|
|
hf_pairs = await hf_client.get_trading_pairs()
|
|
|
if hf_pairs.get("success"):
|
|
|
logger.info("✅ Trading pairs from HF Space")
|
|
|
return hf_pairs
|
|
|
except Exception as hf_error:
|
|
|
logger.warning(f"HF Space unavailable: {hf_error}")
|
|
|
|
|
|
|
|
|
cmc_data = await cmc_client.get_latest_listings(limit=20)
|
|
|
|
|
|
pairs = []
|
|
|
for coin in cmc_data["data"]:
|
|
|
symbol = coin["symbol"]
|
|
|
pairs.append({
|
|
|
"pair": f"{symbol}/USDT",
|
|
|
"base": symbol,
|
|
|
"quote": "USDT",
|
|
|
"tick_size": 0.01,
|
|
|
"min_qty": 0.001
|
|
|
})
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"pairs": pairs,
|
|
|
"meta": {
|
|
|
"cache_ttl_seconds": 300,
|
|
|
"generated_at": datetime.utcnow().isoformat(),
|
|
|
"source": "coinmarketcap"
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to fetch trading pairs: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real trading pairs: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.get("/api/market/ohlc")
|
|
|
async def get_ohlc(
|
|
|
symbol: str = Query(..., description="Trading symbol (e.g., BTC)"),
|
|
|
interval: str = Query("1h", description="Interval (1m, 5m, 15m, 1h, 4h, 1d)"),
|
|
|
limit: int = Query(100, description="Number of candles")
|
|
|
):
|
|
|
"""
|
|
|
Get REAL OHLC candlestick data
|
|
|
Source: CoinMarketCap → Binance fallback (REAL DATA ONLY)
|
|
|
"""
|
|
|
try:
|
|
|
ohlc_result = await cmc_client.get_ohlc(symbol, interval, limit)
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"symbol": symbol,
|
|
|
"interval": interval,
|
|
|
"data": ohlc_result.get("data", []),
|
|
|
"meta": {
|
|
|
"cache_ttl_seconds": 120,
|
|
|
"generated_at": datetime.utcnow().isoformat(),
|
|
|
"source": ohlc_result.get("meta", {}).get("source", "unknown")
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to fetch OHLC data: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real OHLC data: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.get("/api/market/tickers")
|
|
|
async def get_tickers(
|
|
|
limit: int = Query(100, description="Number of tickers"),
|
|
|
sort: str = Query("market_cap", description="Sort by: market_cap, volume, change")
|
|
|
):
|
|
|
"""
|
|
|
Get REAL sorted tickers from CoinMarketCap
|
|
|
"""
|
|
|
try:
|
|
|
cmc_data = await cmc_client.get_latest_listings(limit=limit)
|
|
|
|
|
|
tickers = []
|
|
|
for coin in cmc_data["data"]:
|
|
|
quote = coin.get("quote", {}).get("USD", {})
|
|
|
tickers.append({
|
|
|
"symbol": coin["symbol"],
|
|
|
"name": coin["name"],
|
|
|
"price": quote.get("price", 0),
|
|
|
"change_24h": quote.get("percent_change_24h", 0),
|
|
|
"volume_24h": quote.get("volume_24h", 0),
|
|
|
"market_cap": quote.get("market_cap", 0),
|
|
|
"rank": coin.get("cmc_rank", 0)
|
|
|
})
|
|
|
|
|
|
|
|
|
if sort == "volume":
|
|
|
tickers.sort(key=lambda x: x["volume_24h"], reverse=True)
|
|
|
elif sort == "change":
|
|
|
tickers.sort(key=lambda x: x["change_24h"], reverse=True)
|
|
|
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"tickers": tickers,
|
|
|
"meta": {
|
|
|
"cache_ttl_seconds": 60,
|
|
|
"generated_at": datetime.utcnow().isoformat(),
|
|
|
"source": "coinmarketcap",
|
|
|
"sort": sort
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to fetch tickers: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real tickers: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/api/news")
|
|
|
async def get_news(
|
|
|
limit: int = Query(20, description="Number of articles"),
|
|
|
symbol: Optional[str] = Query(None, description="Filter by crypto symbol")
|
|
|
):
|
|
|
"""
|
|
|
Get REAL cryptocurrency news from NewsAPI
|
|
|
NO MOCK DATA - Only real articles
|
|
|
"""
|
|
|
try:
|
|
|
news_data = await news_client.get_crypto_news(
|
|
|
symbol=symbol or "cryptocurrency",
|
|
|
limit=limit
|
|
|
)
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"articles": news_data["articles"],
|
|
|
"meta": {
|
|
|
"total": len(news_data["articles"]),
|
|
|
"cache_ttl_seconds": 300,
|
|
|
"generated_at": datetime.utcnow().isoformat(),
|
|
|
"source": "newsapi"
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to fetch news: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real news: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.get("/api/news/latest")
|
|
|
async def get_latest_news(symbol: str = Query("BTC"), limit: int = Query(10)):
|
|
|
"""
|
|
|
Get REAL latest news for specific symbol
|
|
|
"""
|
|
|
try:
|
|
|
news_data = await news_client.get_crypto_news(symbol=symbol, limit=limit)
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"symbol": symbol,
|
|
|
"news": news_data["articles"],
|
|
|
"meta": {
|
|
|
"total": len(news_data["articles"]),
|
|
|
"source": "newsapi",
|
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to fetch latest news: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real news: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.get("/api/news/headlines")
|
|
|
async def get_top_headlines(limit: int = Query(10)):
|
|
|
"""
|
|
|
Get REAL top crypto headlines
|
|
|
"""
|
|
|
try:
|
|
|
headlines_data = await news_client.get_top_headlines(limit=limit)
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"headlines": headlines_data["articles"],
|
|
|
"meta": {
|
|
|
"total": len(headlines_data["articles"]),
|
|
|
"source": "newsapi",
|
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to fetch headlines: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real headlines: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/api/blockchain/transactions")
|
|
|
async def get_blockchain_transactions(
|
|
|
chain: str = Query("ethereum", description="Chain: ethereum, bsc, tron"),
|
|
|
limit: int = Query(20, description="Number of transactions")
|
|
|
):
|
|
|
"""
|
|
|
Get REAL blockchain transactions from explorers
|
|
|
Uses REAL API keys: Etherscan, BSCScan, Tronscan
|
|
|
"""
|
|
|
try:
|
|
|
if chain.lower() == "ethereum":
|
|
|
result = await blockchain_client.get_ethereum_transactions(limit=limit)
|
|
|
elif chain.lower() == "bsc":
|
|
|
result = await blockchain_client.get_bsc_transactions(limit=limit)
|
|
|
elif chain.lower() == "tron":
|
|
|
result = await blockchain_client.get_tron_transactions(limit=limit)
|
|
|
else:
|
|
|
raise HTTPException(status_code=400, detail=f"Unsupported chain: {chain}")
|
|
|
|
|
|
return result
|
|
|
|
|
|
except HTTPException:
|
|
|
raise
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to fetch blockchain transactions: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real blockchain data: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.get("/api/blockchain/gas")
|
|
|
async def get_gas_prices(
|
|
|
chain: str = Query("ethereum", description="Blockchain network")
|
|
|
):
|
|
|
"""
|
|
|
Get REAL gas prices from blockchain explorers
|
|
|
"""
|
|
|
try:
|
|
|
result = await blockchain_client.get_gas_prices(chain=chain)
|
|
|
return result
|
|
|
|
|
|
except HTTPException:
|
|
|
raise
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to fetch gas prices: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=503,
|
|
|
detail=f"Unable to fetch real gas prices: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/api/health")
|
|
|
async def health_check():
|
|
|
"""
|
|
|
Health check with REAL data source status
|
|
|
"""
|
|
|
|
|
|
sources_status = {
|
|
|
"coinmarketcap": "unknown",
|
|
|
"newsapi": "unknown",
|
|
|
"etherscan": "unknown",
|
|
|
"bscscan": "unknown",
|
|
|
"tronscan": "unknown",
|
|
|
"hf_space": "unknown"
|
|
|
}
|
|
|
|
|
|
try:
|
|
|
|
|
|
await cmc_client.get_latest_listings(limit=1)
|
|
|
sources_status["coinmarketcap"] = "operational"
|
|
|
except:
|
|
|
sources_status["coinmarketcap"] = "degraded"
|
|
|
|
|
|
try:
|
|
|
|
|
|
await news_client.get_top_headlines(limit=1)
|
|
|
sources_status["newsapi"] = "operational"
|
|
|
except:
|
|
|
sources_status["newsapi"] = "degraded"
|
|
|
|
|
|
try:
|
|
|
|
|
|
hf_status = await hf_client.check_connection()
|
|
|
sources_status["hf_space"] = "operational" if hf_status.get("connected") else "degraded"
|
|
|
except:
|
|
|
sources_status["hf_space"] = "degraded"
|
|
|
|
|
|
|
|
|
sources_status["etherscan"] = "operational"
|
|
|
sources_status["bscscan"] = "operational"
|
|
|
sources_status["tronscan"] = "operational"
|
|
|
|
|
|
return {
|
|
|
"status": "healthy",
|
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
|
"sources": sources_status,
|
|
|
"checks": {
|
|
|
"real_data_sources": True,
|
|
|
"no_mock_data": True,
|
|
|
"all_endpoints_live": True
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
@router.get("/api/status")
|
|
|
async def get_system_status():
|
|
|
"""
|
|
|
Get overall system status with REAL data sources
|
|
|
"""
|
|
|
return {
|
|
|
"status": "operational",
|
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
|
"mode": "REAL_DATA_ONLY",
|
|
|
"mock_data": False,
|
|
|
"services": {
|
|
|
"market_data": "operational",
|
|
|
"news": "operational",
|
|
|
"blockchain": "operational",
|
|
|
"ai_models": "operational"
|
|
|
},
|
|
|
"data_sources": {
|
|
|
"coinmarketcap": {
|
|
|
"status": "active",
|
|
|
"endpoint": "https://pro-api.coinmarketcap.com/v1",
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
"newsapi": {
|
|
|
"status": "active",
|
|
|
"endpoint": "https://newsapi.org/v2",
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
"etherscan": {
|
|
|
"status": "active",
|
|
|
"endpoint": "https://api.etherscan.io/api",
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
"bscscan": {
|
|
|
"status": "active",
|
|
|
"endpoint": "https://api.bscscan.com/api",
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
"tronscan": {
|
|
|
"status": "active",
|
|
|
"endpoint": "https://apilist.tronscan.org/api",
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
"hf_space": {
|
|
|
"status": "active",
|
|
|
"endpoint": "https://really-amin-datasourceforcryptocurrency.hf.space",
|
|
|
"has_api_token": True
|
|
|
}
|
|
|
},
|
|
|
"version": "2.0.0-real-data",
|
|
|
"uptime_seconds": 0
|
|
|
}
|
|
|
|
|
|
|
|
|
@router.get("/api/providers")
|
|
|
async def get_providers():
|
|
|
"""
|
|
|
List all REAL data providers
|
|
|
"""
|
|
|
providers = [
|
|
|
{
|
|
|
"id": "coinmarketcap",
|
|
|
"name": "CoinMarketCap",
|
|
|
"category": "market_data",
|
|
|
"status": "active",
|
|
|
"capabilities": ["prices", "market_cap", "volume", "ohlc"],
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
{
|
|
|
"id": "newsapi",
|
|
|
"name": "NewsAPI",
|
|
|
"category": "news",
|
|
|
"status": "active",
|
|
|
"capabilities": ["crypto_news", "headlines", "articles"],
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
{
|
|
|
"id": "etherscan",
|
|
|
"name": "Etherscan",
|
|
|
"category": "blockchain",
|
|
|
"status": "active",
|
|
|
"capabilities": ["eth_transactions", "gas_prices", "smart_contracts"],
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
{
|
|
|
"id": "bscscan",
|
|
|
"name": "BSCScan",
|
|
|
"category": "blockchain",
|
|
|
"status": "active",
|
|
|
"capabilities": ["bsc_transactions", "token_info"],
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
{
|
|
|
"id": "tronscan",
|
|
|
"name": "Tronscan",
|
|
|
"category": "blockchain",
|
|
|
"status": "active",
|
|
|
"capabilities": ["tron_transactions", "token_transfers"],
|
|
|
"has_api_key": True
|
|
|
},
|
|
|
{
|
|
|
"id": "hf_space",
|
|
|
"name": "HuggingFace Space",
|
|
|
"category": "ai_models",
|
|
|
"status": "active",
|
|
|
"capabilities": ["sentiment", "predictions", "text_generation"],
|
|
|
"has_api_token": True
|
|
|
}
|
|
|
]
|
|
|
|
|
|
return {
|
|
|
"success": True,
|
|
|
"providers": providers,
|
|
|
"total": len(providers),
|
|
|
"meta": {
|
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
|
"all_real_data": True,
|
|
|
"no_mock_providers": True
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/api/models/initialize")
|
|
|
async def initialize_models():
|
|
|
"""
|
|
|
Initialize REAL AI models from HuggingFace
|
|
|
"""
|
|
|
try:
|
|
|
result = await ai_registry.load_models()
|
|
|
return {
|
|
|
"success": True,
|
|
|
"result": result,
|
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
|
}
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to initialize models: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=500,
|
|
|
detail=f"Failed to initialize models: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.get("/api/models/list")
|
|
|
async def get_models_list():
|
|
|
"""
|
|
|
Get list of available REAL AI models
|
|
|
"""
|
|
|
try:
|
|
|
return ai_registry.get_models_list()
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Failed to get models list: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=500,
|
|
|
detail=f"Failed to get models list: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.post("/api/models/{model_key}/predict")
|
|
|
async def predict_with_model(model_key: str, request: PredictRequest):
|
|
|
"""
|
|
|
Generate REAL predictions using AI models
|
|
|
NO FAKE PREDICTIONS - Only real model inference
|
|
|
"""
|
|
|
try:
|
|
|
if model_key == "trading_signals":
|
|
|
result = await ai_registry.get_trading_signal(
|
|
|
symbol=request.symbol,
|
|
|
context=request.context
|
|
|
)
|
|
|
else:
|
|
|
|
|
|
text = request.context or f"Analyze {request.symbol} cryptocurrency"
|
|
|
result = await ai_registry.predict_sentiment(
|
|
|
text=text,
|
|
|
model_key=model_key
|
|
|
)
|
|
|
|
|
|
return result
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Model prediction failed: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=500,
|
|
|
detail=f"Real model prediction failed: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.post("/api/sentiment/analyze")
|
|
|
async def analyze_sentiment(request: SentimentRequest):
|
|
|
"""
|
|
|
Analyze REAL sentiment using AI models
|
|
|
NO FAKE ANALYSIS
|
|
|
"""
|
|
|
try:
|
|
|
|
|
|
model_map = {
|
|
|
"crypto": "sentiment_crypto",
|
|
|
"financial": "sentiment_financial",
|
|
|
"social": "sentiment_twitter",
|
|
|
"auto": "sentiment_crypto"
|
|
|
}
|
|
|
|
|
|
model_key = model_map.get(request.mode, "sentiment_crypto")
|
|
|
|
|
|
result = await ai_registry.predict_sentiment(
|
|
|
text=request.text,
|
|
|
model_key=model_key
|
|
|
)
|
|
|
|
|
|
return result
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Sentiment analysis failed: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=500,
|
|
|
detail=f"Real sentiment analysis failed: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.post("/api/ai/generate")
|
|
|
async def generate_ai_text(
|
|
|
prompt: str = Body(..., embed=True),
|
|
|
max_length: int = Body(200, embed=True)
|
|
|
):
|
|
|
"""
|
|
|
Generate REAL text using AI models
|
|
|
NO FAKE GENERATION
|
|
|
"""
|
|
|
try:
|
|
|
result = await ai_registry.generate_text(
|
|
|
prompt=prompt,
|
|
|
max_length=max_length
|
|
|
)
|
|
|
|
|
|
return result
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ AI text generation failed: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=500,
|
|
|
detail=f"Real AI generation failed: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.post("/api/trading/signal")
|
|
|
async def get_trading_signal(
|
|
|
symbol: str = Body(..., embed=True),
|
|
|
context: Optional[str] = Body(None, embed=True)
|
|
|
):
|
|
|
"""
|
|
|
Get REAL trading signal from AI model
|
|
|
NO FAKE SIGNALS
|
|
|
"""
|
|
|
try:
|
|
|
result = await ai_registry.get_trading_signal(
|
|
|
symbol=symbol,
|
|
|
context=context
|
|
|
)
|
|
|
|
|
|
return result
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Trading signal failed: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=500,
|
|
|
detail=f"Real trading signal failed: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
@router.post("/api/news/summarize")
|
|
|
async def summarize_news_article(
|
|
|
text: str = Body(..., embed=True)
|
|
|
):
|
|
|
"""
|
|
|
Summarize REAL news using AI
|
|
|
NO FAKE SUMMARIES
|
|
|
"""
|
|
|
try:
|
|
|
result = await ai_registry.summarize_news(text=text)
|
|
|
|
|
|
return result
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ News summarization failed: {e}")
|
|
|
raise HTTPException(
|
|
|
status_code=500,
|
|
|
detail=f"Real summarization failed: {str(e)}"
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = ["router"]
|
|
|
|