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.
211 lines
6.6 KiB
Python
211 lines
6.6 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
|
|
from app.models import news_headline # News v2 model
|
|
from app.models import gainer_snapshot # Gainer intraday snapshots
|
|
|
|
# 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
|
|
# Start SEC ingest scheduler (Form 4 + 13D/G daily/weekly)
|
|
try:
|
|
from app.services.sec_ingest.scheduler import start_sec_ingest_scheduler
|
|
start_sec_ingest_scheduler()
|
|
except Exception:
|
|
pass
|
|
# Start News v2 ingest scheduler (Alpaca News + StockTwits + Finnhub)
|
|
try:
|
|
from app.services.news.scheduler import start_news_ingest_scheduler
|
|
start_news_ingest_scheduler()
|
|
except Exception:
|
|
pass
|
|
# Start gainer snapshot scheduler (5-min intraday during market hours)
|
|
try:
|
|
from app.services.gainers.scheduler import start_gainer_scheduler
|
|
start_gainer_scheduler()
|
|
except Exception:
|
|
pass
|
|
yield
|
|
# Shutdown
|
|
try:
|
|
from app.services.overlay.scheduler import stop_scheduler
|
|
stop_scheduler()
|
|
except Exception:
|
|
pass
|
|
try:
|
|
from app.services.sec_ingest.scheduler import stop_sec_ingest_scheduler
|
|
stop_sec_ingest_scheduler()
|
|
except Exception:
|
|
pass
|
|
try:
|
|
from app.services.news.scheduler import stop_news_ingest_scheduler
|
|
stop_news_ingest_scheduler()
|
|
except Exception:
|
|
pass
|
|
try:
|
|
from app.services.gainers.scheduler import stop_gainer_scheduler
|
|
stop_gainer_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": "Overlay headlines — Yahoo RSS headline collector"
|
|
},
|
|
{
|
|
"name": "overlay-admin",
|
|
"description": "Overlay 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": "ownership",
|
|
"description": "SEC 13D/13G activist ownership events — activist filings, active positions (PIT-safe)"
|
|
},
|
|
{
|
|
"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
|
|
) |