workspace/projects/trading-platform/apps/data-service/src/app.py
rckrdmrd 789d1ab46b
Some checks failed
CI Pipeline / changes (push) Has been cancelled
CI Pipeline / core (push) Has been cancelled
CI Pipeline / trading-backend (push) Has been cancelled
CI Pipeline / trading-data-service (push) Has been cancelled
CI Pipeline / trading-frontend (push) Has been cancelled
CI Pipeline / erp-core (push) Has been cancelled
CI Pipeline / erp-mecanicas (push) Has been cancelled
CI Pipeline / gamilit-backend (push) Has been cancelled
CI Pipeline / gamilit-frontend (push) Has been cancelled
changes on workspace
2025-12-09 14:46:20 -06:00

201 lines
5.3 KiB
Python

"""
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"
)