Spaces:
Running
Running
| """ | |
| QuantHedge β FastAPI Application Entry Point. | |
| Production-grade quantitative research and hedge simulation platform. | |
| """ | |
| from __future__ import annotations | |
| import asyncio | |
| import logging | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from app.config import get_settings | |
| from app.database import Base, engine | |
| # Configure logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", | |
| datefmt="%Y-%m-%d %H:%M:%S", | |
| ) | |
| logger = logging.getLogger("quanthedge") | |
| async def lifespan(app: FastAPI): | |
| """Application startup and shutdown events.""" | |
| settings = get_settings() | |
| logger.info("Starting %s v%s", settings.app_name, settings.app_version) | |
| # Create database tables (with timeout to prevent hanging on unreachable DB) | |
| try: | |
| async def _init_db(): | |
| async with engine.begin() as conn: | |
| from app.models import ( # noqa: F401 β force model registration | |
| Asset, | |
| BacktestResult, | |
| Exchange, | |
| Factor, | |
| FactorExposure, | |
| FeatureData, | |
| Holding, | |
| MarketplaceEntry, | |
| NewsArticle, | |
| Portfolio, | |
| PriceData, | |
| ResearchReport, | |
| Signal, | |
| Strategy, | |
| User, | |
| AITradeDecision, | |
| PaperTradingConfig, | |
| ) | |
| await conn.run_sync(Base.metadata.create_all) | |
| logger.info("Database tables created successfully") | |
| # Safe migration: add new columns if they don't exist | |
| from sqlalchemy import text | |
| migration_cols = [ | |
| ("ai_trade_decisions", "exit_price", "FLOAT"), | |
| ("ai_trade_decisions", "realized_pnl", "FLOAT"), | |
| ("ai_trade_decisions", "realized_pnl_pct", "FLOAT"), | |
| ("ai_trade_decisions", "trade_duration_hours", "FLOAT"), | |
| ("ai_trade_decisions", "regime_at_entry", "VARCHAR(30)"), | |
| ("ai_trade_decisions", "outcome", "VARCHAR(10)"), | |
| ] | |
| for table, col, dtype in migration_cols: | |
| try: | |
| await conn.execute(text( | |
| f"ALTER TABLE {table} ADD COLUMN IF NOT EXISTS {col} {dtype}" | |
| )) | |
| except Exception: | |
| pass # Column already exists or DB doesn't support IF NOT EXISTS | |
| await asyncio.wait_for(_init_db(), timeout=15) | |
| except asyncio.TimeoutError: | |
| logger.warning("Database connection timed out after 15s β starting without DB") | |
| except Exception as e: | |
| logger.warning("Table creation skipped (tables may already exist): %s", e) | |
| # Seed exchange data (with timeout) | |
| try: | |
| await asyncio.wait_for(_seed_exchanges(), timeout=10) | |
| except (asyncio.TimeoutError, Exception) as e: | |
| logger.warning("Exchange seeding skipped: %s", e) | |
| # Initialize cache | |
| from app.redis_client import get_redis | |
| await get_redis() | |
| logger.info("Application ready β serving on all configured routers") | |
| yield | |
| # Cleanup | |
| await engine.dispose() | |
| logger.info("Application shutdown complete") | |
| async def _seed_exchanges(): | |
| """Seed exchange metadata on startup if not present.""" | |
| from sqlalchemy import select | |
| from app.database import async_session_factory | |
| from app.models.market import Exchange | |
| from app.services.data_ingestion.market_metadata import EXCHANGES | |
| async with async_session_factory() as session: | |
| result = await session.execute(select(Exchange).limit(1)) | |
| if result.scalar_one_or_none() is None: | |
| for exc_data in EXCHANGES: | |
| exchange = Exchange(**exc_data) | |
| session.add(exchange) | |
| await session.commit() | |
| logger.info("Seeded %d exchanges", len(EXCHANGES)) | |
| def create_app() -> FastAPI: | |
| """Application factory.""" | |
| settings = get_settings() | |
| app = FastAPI( | |
| title=settings.app_name, | |
| version=settings.app_version, | |
| description=( | |
| "QuantHedge β Global quantitative research and hedge simulation platform. " | |
| "Provides financial data engineering, factor modeling, signal generation, " | |
| "strategy research, portfolio optimization, backtesting, and AI-assisted insights." | |
| ), | |
| docs_url="/docs", | |
| redoc_url="/redoc", | |
| lifespan=lifespan, | |
| ) | |
| # CORS | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=settings.cors_origin_list, | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Mount routers | |
| from app.routers import auth, backtests, calendar, copilot, data, holdings, marketplace, ml, patterns, pinescript, portfolios, quant, research, sentiment, strategies | |
| from app.routers import paper_trading as paper_trading_router | |
| app.include_router(auth.router, prefix="/api") | |
| app.include_router(data.router, prefix="/api") | |
| app.include_router(quant.router, prefix="/api") | |
| app.include_router(strategies.router, prefix="/api") | |
| app.include_router(portfolios.router, prefix="/api") | |
| app.include_router(backtests.router, prefix="/api") | |
| app.include_router(marketplace.router, prefix="/api") | |
| app.include_router(research.router, prefix="/api") | |
| app.include_router(ml.router, prefix="/api") | |
| app.include_router(holdings.router) | |
| app.include_router(sentiment.router) | |
| app.include_router(calendar.router) | |
| app.include_router(copilot.router) | |
| app.include_router(patterns.router, prefix="/api") | |
| app.include_router(pinescript.router, prefix="/api") | |
| app.include_router(paper_trading_router.router) | |
| # Health check | |
| async def health(): | |
| return { | |
| "status": "healthy", | |
| "app": settings.app_name, | |
| "version": settings.app_version, | |
| } | |
| return app | |
| app = create_app() | |