""" FastAPI Application OrbiQuant IA Trading Platform - Data Service Main application entry point with REST API and WebSocket support. """ import asyncio import logging import signal from contextlib import asynccontextmanager from datetime import datetime from typing import Optional import asyncpg from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from config import Config from api.routes import router as api_router from websocket.handlers import WSRouter, set_ws_manager from websocket.manager import WebSocketManager from providers.polygon_client import PolygonClient from providers.binance_client import BinanceClient # Logging setup logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """Application lifespan manager.""" config = Config.from_env() # Store config app.state.config = config # Initialize database pool logger.info("Connecting to database...") app.state.db_pool = await asyncpg.create_pool( config.database.dsn, min_size=config.database.min_connections, max_size=config.database.max_connections ) logger.info("Database connection pool created") # Initialize Polygon client if config.polygon.api_key: app.state.polygon_client = PolygonClient( api_key=config.polygon.api_key, rate_limit_per_min=config.polygon.rate_limit_per_min, base_url=config.polygon.base_url ) logger.info("Polygon client initialized") else: app.state.polygon_client = None # Initialize Binance client import os binance_key = os.getenv("BINANCE_API_KEY") binance_secret = os.getenv("BINANCE_API_SECRET") if binance_key: app.state.binance_client = BinanceClient( api_key=binance_key, api_secret=binance_secret, testnet=os.getenv("BINANCE_TESTNET", "false").lower() == "true" ) logger.info("Binance client initialized") else: app.state.binance_client = None # Initialize WebSocket manager ws_manager = WebSocketManager() await ws_manager.start() app.state.ws_manager = ws_manager set_ws_manager(ws_manager) logger.info("WebSocket manager started") # Store start time for uptime app.state.start_time = datetime.utcnow() logger.info("Data Service started successfully") yield # Application runs here # Shutdown logger.info("Shutting down Data Service...") await ws_manager.stop() if app.state.binance_client: await app.state.binance_client.close() await app.state.db_pool.close() logger.info("Data Service shutdown complete") def create_app() -> FastAPI: """Create and configure FastAPI application.""" app = FastAPI( title="OrbiQuant Data Service", description=""" Market data service for the OrbiQuant IA Trading Platform. ## Features - Real-time ticker prices - Historical OHLCV data - Order book snapshots - WebSocket streaming - Multi-provider support (Polygon, Binance, MT4) ## WebSocket Channels - `ticker` - Real-time price updates - `candles` - OHLCV candle updates - `orderbook` - Order book snapshots - `trades` - Recent trades - `signals` - ML trading signals """, version="1.0.0", docs_url="/docs", redoc_url="/redoc", lifespan=lifespan ) # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=[ "http://localhost:3000", "http://localhost:3001", "http://localhost:5173", "https://orbiquant.com", "https://*.orbiquant.com", ], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Global exception handler @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): logger.error(f"Unhandled exception: {exc}", exc_info=True) return JSONResponse( status_code=500, content={ "error": "Internal server error", "detail": str(exc) if app.debug else "An unexpected error occurred", "timestamp": datetime.utcnow().isoformat() } ) # Include routers app.include_router(api_router) # MT4/MetaAPI routes from api.mt4_routes import router as mt4_router app.include_router(mt4_router) # WebSocket router ws_router = WSRouter() app.include_router(ws_router.router, tags=["WebSocket"]) # Root endpoint @app.get("/", tags=["Root"]) async def root(): return { "service": "OrbiQuant Data Service", "version": "1.0.0", "status": "running", "docs": "/docs", "health": "/health", "websocket": "/ws/stream", "mt4": "/api/mt4/status" } return app # Create application instance app = create_app() if __name__ == "__main__": import uvicorn uvicorn.run( "app:app", host="0.0.0.0", port=8001, reload=True, log_level="info" )