You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
5.3 KiB
Python

"""
Main FastAPI application
"""
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.gzip import GZipMiddleware
from fastapi.responses import RedirectResponse
from app.core.config import settings
from app.api.v1.api import api_router
from app.core.database import engine, Base
from app.middleware.error_logger import ErrorLoggingMiddleware, start_request_log_flusher
from app.models import error_log, request_log, fred_data, filing, finra_short_volume, alpaca_price # Import to register models
from app.models import overlay_registry, overlay_raw_event, overlay_feature # Overlay models
# Create database tables
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup - ensure tables exist
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# Start async batch request log flusher
start_request_log_flusher()
# Start overlay batch scheduler (graceful no-op if apscheduler not installed)
try:
from app.services.overlay.scheduler import start_scheduler
start_scheduler()
except Exception:
pass
yield
# Shutdown
try:
from app.services.overlay.scheduler import stop_scheduler
stop_scheduler()
except Exception:
pass
try:
from app.core.http_client import close_http_session
await close_http_session()
except Exception:
pass
await engine.dispose()
# Create FastAPI app
app = FastAPI(
title=settings.APP_NAME,
version=settings.APP_VERSION,
openapi_url=f"{settings.API_PREFIX}/openapi.json",
docs_url=f"{settings.API_PREFIX}/docs",
redoc_url=f"{settings.API_PREFIX}/redoc",
lifespan=lifespan
)
# Add error logging middleware
app.add_middleware(ErrorLoggingMiddleware)
# Set up CORS - use configured origins if available, otherwise allow all
cors_origins = [str(o) for o in settings.BACKEND_CORS_ORIGINS] if settings.BACKEND_CORS_ORIGINS else ["*"]
app.add_middleware(
CORSMiddleware,
allow_origins=cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# GZip compression for responses > 1KB
app.add_middleware(GZipMiddleware, minimum_size=1000)
# Include API router
app.include_router(api_router, prefix=settings.API_PREFIX)
# Root — redirect to Swagger UI
@app.get("/", include_in_schema=False)
async def root_redirect():
return RedirectResponse(url=f"{settings.API_PREFIX}/docs")
# Additional metadata for OpenAPI
app.openapi_tags = [
{
"name": "health",
"description": "Health check endpoints"
},
{
"name": "financial",
"description": "Financial data retrieval endpoints"
},
{
"name": "price",
"description": "Price data endpoints (OHLCV)"
},
{
"name": "news",
"description": "News and social media endpoints"
},
{
"name": "metadata",
"description": "Data catalog and metadata endpoints"
},
{
"name": "filings",
"description": "SEC filings search, document listing, and exhibit extraction (8-K, 6-K, 20-F, 40-F)"
},
{
"name": "etf",
"description": "ETF holdings endpoints"
},
{
"name": "alpaca",
"description": "Alpaca Market Data endpoints (OHLCV bars, connection status)"
},
{
"name": "finra",
"description": "FINRA RegSHO short sale volume data (ingest, query, ratio history)"
},
{
"name": "admin",
"description": "Administrative endpoints (migration, etc.)"
},
{
"name": "overlay",
"description": "Attention Overlay - retail investor interest, media diffusion, crowding signals"
},
{
"name": "overlay-admin",
"description": "Overlay administrative endpoints (pipeline trigger, health, job log)"
},
{
"name": "screener",
"description": "Stock screener — condition-based filtering by market cap, volume, price, P/E, sector, exchange"
},
{
"name": "stocks",
"description": "Stock market data — most active, 52-week gainers, trending, and index constituents (S&P 500 / Nasdaq 100)"
},
{
"name": "attention",
"description": "Attention signals — Wikipedia pageview spikes and GDELT news article counts for event-centric backtesting"
},
{
"name": "attention-admin",
"description": "Attention administrative endpoints — entity resolution, Wikipedia and GDELT data collection"
},
{
"name": "database",
"description": "Database inspection — record counts, date ranges, raw data browsing, and ETF snapshot history"
},
{
"name": "fred",
"description": "FRED (Federal Reserve Economic Data) — macroeconomic series via FRED API proxy"
},
{
"name": "error-logs",
"description": "Error log management — browse and clear server-side error records"
},
{
"name": "request-logs",
"description": "Request log management — browse API request history and latency records"
}
]
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=settings.API_PORT,
reload=settings.DEBUG
)