quanthedge / backend /app /main.py
jashdoshi77's picture
made the brain smarted and added option chain
8c7bd57
"""
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")
@asynccontextmanager
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
@app.get("/api/health")
async def health():
return {
"status": "healthy",
"app": settings.app_name,
"version": settings.app_version,
}
return app
app = create_app()