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.

311 lines
13 KiB
Python

"""
Main FastAPI application
"""
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import RedirectResponse, HTMLResponse
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
from app.models import error_log, request_log, fred_data, filing # Import to register 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)
yield
# Shutdown
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=["*"],
)
# Include API router
app.include_router(api_router, prefix=settings.API_PREFIX)
# Root documentation endpoint
@app.get("/", response_class=HTMLResponse, include_in_schema=False)
async def root_documentation():
"""
Display comprehensive API documentation at root path
"""
try:
# Simple working version
simple_html = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stock Oracle API Documentation</title>
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
color: #333;
}}
h1 {{ color: #007acc; border-bottom: 2px solid #007acc; padding-bottom: 10px; }}
h2 {{ color: #2c3e50; margin-top: 2em; }}
.nav-links {{ margin: 20px 0; }}
.nav-links a {{
display: inline-block;
margin-right: 15px;
padding: 10px 20px;
background: #007acc;
color: white;
text-decoration: none;
border-radius: 5px;
}}
.nav-links a:hover {{ background: #0066aa; }}
code {{ background: #f4f4f4; padding: 2px 4px; border-radius: 3px; }}
pre {{ background: #f4f4f4; padding: 15px; border-radius: 5px; overflow-x: auto; }}
.new-badge {{ background: #e74c3c; color: white; padding: 2px 6px; border-radius: 3px; font-size: 0.8em; }}
</style>
</head>
<body>
<h1>🔮 Stock Oracle API Documentation</h1>
<p><strong>Comprehensive Investment Data Analysis API</strong></p>
<div class="nav-links">
<a href="{settings.API_PREFIX}/docs">📋 Interactive API Docs</a>
<a href="{settings.API_PREFIX}/redoc">📖 ReDoc</a>
<a href="{settings.API_PREFIX}/health">💚 Health Check</a>
<a href="{settings.API_PREFIX}/openapi.json">⚙️ OpenAPI Schema</a>
</div>
<h2>🚀 Quick Start</h2>
<p><strong>Base URL:</strong> <code>http://localhost:18001/api/v1</code></p>
<h2>🎯 Main Endpoints</h2>
<h3>Health & System</h3>
<ul>
<li><code>GET /health</code> - API health status</li>
<li><code>GET /health/detailed</code> - Detailed system health</li>
</ul>
<h3>Financial Data</h3>
<ul>
<li><code>POST /financial/data</code> - Get comprehensive financial data</li>
<li><code>GET /financial/data/{{ticker}}</code> - Simple financial data</li>
<li><code>POST /financial/data/bulk</code> - Bulk financial data</li>
</ul>
<h3>Price Data</h3>
<ul>
<li><code>POST /price/data</code> - Get historical price data (OHLCV)</li>
<li><code>GET /price/data/{{ticker}}</code> - Simple price data</li>
<li><code>POST /price/data/bulk</code> - Bulk price data</li>
<li><code>GET /price/quote/{{ticker}}</code> - Latest quote (regular/pre/post market)</li>
<li><code>GET /price/intraday/{{ticker}}</code> - Intraday candles (interval, period)</li>
<li><code>GET /price/today/{{ticker}}</code> - Today's OHLC (daily or 1m aggregate)</li>
</ul>
<h3>Stock Market Data <span class="new-badge">NEW</span></h3>
<ul>
<li><code>GET /stocks/trending</code> - Trending stocks with intelligent parameter coordination (n=500 default)</li>
<li><code>GET /stocks/most-active</code> - Most actively traded stocks</li>
<li><code>GET /stocks/52-week-gainers</code> - Top 52-week gaining stocks</li>
</ul>
<h3>FRED Economic Data <span class="new-badge">NEW</span></h3>
<ul>
<li><code>GET /fred/proxy/{{endpoint}}</code> - Universal FRED API proxy with caching</li>
<li><code>GET /fred/endpoints</code> - List all supported FRED API endpoints</li>
<li><code>GET /fred/stats/usage</code> - API usage statistics and monitoring</li>
</ul>
<h3>News & Social Media <span class="new-badge">NEW</span></h3>
<ul>
<li><code>GET /news/{{ticker}}</code> - Complete news and social media data</li>
<li><code>GET /news/{{ticker}}/news-only</code> - News articles only (faster)</li>
<li><code>GET /news/{{ticker}}/social-only</code> - Social media posts only</li>
</ul>
<h3>SEC Filings <span class="new-badge">NEW</span></h3>
<ul>
<li><code>GET /filings/search/{{ticker}}</code> - Search SEC filings (8-K, 6-K, 20-F, 40-F, 10-K, 10-Q)</li>
<li><code>GET /filings/documents/{{accession_number}}</code> - List all documents in a filing</li>
<li><code>GET /filings/exhibit/{{accession_number}}</code> - Extract exhibit content (e.g., EX-99.1 press releases)</li>
</ul>
<h3>ETF Holdings</h3>
<ul>
<li><code>GET /etf/holdings/{{ticker}}</code> - ETF holdings at date or most recent</li>
<li><code>POST /etf/admin/refresh-maps</code> - Refresh CUSIP/CIK maps</li>
</ul>
<h2>📊 Example Requests</h2>
<h3>Financial Data</h3>
<pre><code>curl -X POST "http://localhost:18001/api/v1/financial/data" \\
-H "Content-Type: application/json" \\
-d '{{"ticker": "AAPL", "period": "1y", "include_metrics": true}}'</code></pre>
<h3>Trending Stocks <span class="new-badge">NEW</span></h3>
<pre><code># Get trending stocks (default: 500 total stocks with intelligent coordination)
curl "http://localhost:18001/api/v1/stocks/trending"
# Custom total count
curl "http://localhost:18001/api/v1/stocks/trending?n=200"</code></pre>
<h3>FRED Economic Data <span class="new-badge">NEW</span></h3>
<pre><code># Get GDP series information
curl "http://localhost:18001/api/v1/fred/proxy/series?series_id=GDP"
# Get unemployment rate observations
curl "http://localhost:18001/api/v1/fred/proxy/series/observations?series_id=UNRATE&limit=12"</code></pre>
<h3>News & Social Data <span class="new-badge">NEW</span></h3>
<pre><code>curl "http://localhost:18001/api/v1/news/AAPL?days_back=7&max_articles=20"</code></pre>
<h3>Price - Quote/Intraday/Today <span class="new-badge">NEW</span></h3>
<pre><code># Quote (latest regular/pre/post)
curl "http://localhost:18001/api/v1/price/quote/AAPL?use_prepost=true"
# Intraday 1m candles for 1 day
curl "http://localhost:18001/api/v1/price/intraday/AAPL?interval=1m&period=1d"
# Today's OHLC (daily if available; otherwise 1m aggregate)
curl "http://localhost:18001/api/v1/price/today/AAPL"</code></pre>
<h3>SEC Filings <span class="new-badge">NEW</span></h3>
<pre><code># Search AAPL 8-K filings (earnings announcements, material events)
curl "http://localhost:18001/api/v1/filings/search/AAPL?form_type=8-K&limit=5"
# Search foreign issuer filings
curl "http://localhost:18001/api/v1/filings/search/TSM?form_type=20-F"
curl "http://localhost:18001/api/v1/filings/search/SAP?form_type=6-K"
# List all documents in a filing
curl "http://localhost:18001/api/v1/filings/documents/0000320193-26-000005"
# Extract press release (Exhibit 99.1) from an 8-K
curl "http://localhost:18001/api/v1/filings/exhibit/0000320193-26-000005?exhibit_type=EX-99.1"</code></pre>
<h3>ETF Holdings</h3>
<pre><code>curl "http://localhost:18001/api/v1/etf/holdings/QQQ"</code></pre>
<h2>🐍 Python Client</h2>
<pre><code>from stock_oracle_client import StockOracleClient
client = StockOracleClient("http://localhost:18001")
# Check health
health = client.get_health()
print("API Status:", health["status"])
# Get financial data
data = client.get_financial_data("AAPL", period="1y")
# Get news data (NEW!)
news = client.get_news_social_data("AAPL", days_back=7)</code></pre>
<h2>📈 Key Features</h2>
<ul>
<li><strong>SEC EDGAR Data</strong> - Official company filings (10-K, 10-Q) with XBRL financial data</li>
<li><strong>SEC Filings Search</strong> - Index and search 8-K, 6-K, 20-F, 40-F filings with exhibit extraction</li>
<li><strong>Real-time News</strong> - Yahoo Finance + NewsAPI integration</li>
<li><strong>Social Sentiment</strong> - Reddit discussions and sentiment analysis</li>
<li><strong>ETF Holdings</strong> - Complete ETF portfolio analysis via N-PORT</li>
<li><strong>Price Data</strong> - Historical OHLCV data from Yahoo Finance</li>
<li><strong>Investment Metrics</strong> - P/E, ROE, debt ratios, growth metrics</li>
</ul>
<h2>🔧 Data Sources</h2>
<ul>
<li><strong>SEC EDGAR</strong> - Official company filings and ETF holdings</li>
<li><strong>Yahoo Finance</strong> - Price data and financial news (via yfinance_plus)</li>
<li><strong>NewsAPI</strong> - Professional news aggregation</li>
<li><strong>Reddit API</strong> - Social media sentiment from investing subreddits</li>
</ul>
<footer style="margin-top: 3em; padding-top: 2em; border-top: 1px solid #eee; text-align: center; color: #666;">
<p><strong>Stock Oracle API</strong> - Built with FastAPI, powered by SEC EDGAR data</p>
<p>For complete interactive documentation, visit <a href="{settings.API_PREFIX}/docs">Swagger UI</a></p>
<p><small>Version {settings.APP_VERSION}</small></p>
</footer>
</body>
</html>
"""
return HTMLResponse(content=simple_html)
except Exception as e:
# Fallback to Swagger UI if anything goes wrong
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": "admin",
"description": "Administrative endpoints (migration, etc.)"
}
]
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=settings.API_PORT,
reload=settings.DEBUG
)