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.

443 lines
19 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, 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 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
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>Alpaca Market Data <span class="new-badge">NEW</span></h3>
<ul>
<li><code>GET /alpaca/status</code> - Alpaca connection status and key validity</li>
<li><code>GET /alpaca/bars/{{ticker}}</code> - Fetch bars directly from Alpaca (raw, no DB)</li>
</ul>
<h3>FINRA Short Volume <span class="new-badge">NEW</span></h3>
<ul>
<li><code>GET /finra/short-volume/{{symbol}}</code> - Short sale volume data for a symbol</li>
<li><code>GET /finra/short-ratio/{{symbol}}</code> - Short ratio history (aggregated)</li>
<li><code>POST /finra/admin/ingest</code> - Manually ingest FINRA data for a date/range</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>
<h3>Attention Overlay <span class="new-badge">NEW</span></h3>
<ul>
<li><code>GET /overlay/{{symbol}}</code> - Overlay score + features + source details (0~1 score, band, hints)</li>
<li><code>GET /overlay/bulk?symbols=AAPL,TSLA,NVDA</code> - Bulk overlay scores (max 50 symbols)</li>
<li><code>GET /overlay/top-movers</code> - Symbols with highest overlay scores in last 24h</li>
<li><code>GET /overlay/{{symbol}}/headlines</code> - Recent news headlines matched to symbol</li>
<li><code>GET /overlay/{{symbol}}/youtube</code> - YouTube video mentions from investing channels</li>
<li><code>GET /overlay/{{symbol}}/wiki</code> - Wikipedia pageview time series</li>
<li><code>GET /overlay/{{symbol}}/crowding</code> - FINRA short-sale crowding metrics</li>
<li><code>GET /overlay/{{symbol}}/trends</code> - Google Trends interest data (if enabled)</li>
<li><code>GET /overlay/{{symbol}}/history</code> - Historical overlay scores (backtesting)</li>
<li><code>GET /overlay/admin/health</code> - Data source health and last collection status</li>
<li><code>POST /overlay/admin/trigger-pipeline</code> - Manually trigger full data collection + scoring</li>
<li><code>GET /overlay/admin/job-log</code> - Pipeline job execution log</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>
<li><strong>Attention Overlay</strong> - Multi-source retail interest signal (news burst, Wikipedia views, YouTube mentions, FINRA crowding)</li>
</ul>
<h3>Attention Overlay Response Example <span class="new-badge">NEW</span></h3>
<pre><code>GET /api/v1/overlay/AAPL
{{
"symbol": "AAPL",
"as_of_ts": "2026-03-13T20:30:00Z",
"overlay_score": 0.72, // 0~1 (higher = more retail attention)
"overlay_confidence": 0.80, // fraction of sources with data
"overlay_band": "supportive", // silent / tepid / supportive / loud / frenzied
"hold_extension_hint": "neutral", // extend / neutral / trim
"add_on_eligibility": false,
"features": {{
"headline_burst_z": 1.2, // z-score vs 30-day window
"youtube_influence_z": 0.8,
"wiki_attention_z": null,
"theme_heat_z": null,
"crowding_stress_z": -0.3
}},
"source_presence": {{
"yahoo": true, "youtube": true,
"wikimedia": false, "google_trends": false, "finra": true
}},
"source_details": {{
"yahoo": {{"headline_count_6h": 5, "headline_count_24h": 12, "publisher_breadth_24h": 4}},
"finra": {{"short_volume_ratio": 0.42, "short_volume_spike_zscore": -0.3}}
}}
}}</code></pre>
<h3>Alpaca Market Data <span class="new-badge">NEW</span></h3>
<pre><code># Check Alpaca connection status
curl "http://localhost:18001/api/v1/alpaca/status"
# Get daily bars from Alpaca
curl "http://localhost:18001/api/v1/alpaca/bars/AAPL?interval=1d&start_date=2025-01-01&end_date=2025-01-31"</code></pre>
<h3>FINRA Short Volume <span class="new-badge">NEW</span></h3>
<pre><code># Ingest FINRA data for a specific date
curl -X POST "http://localhost:18001/api/v1/finra/admin/ingest?date=2025-03-10"
# Get short volume for AAPL (last 30 days)
curl "http://localhost:18001/api/v1/finra/short-volume/AAPL?days=30"
# Get short ratio history
curl "http://localhost:18001/api/v1/finra/short-ratio/AAPL?days=60"</code></pre>
<h3>Attention Overlay <span class="new-badge">NEW</span></h3>
<pre><code># Overlay score for a single symbol
curl "http://localhost:18001/api/v1/overlay/AAPL"
# Bulk scores (comma-separated, max 50)
curl "http://localhost:18001/api/v1/overlay/bulk?symbols=AAPL,TSLA,NVDA,AMD,META"
# Top movers by overlay score (last 24h)
curl "http://localhost:18001/api/v1/overlay/top-movers?limit=10"
# Recent headlines matched to AAPL
curl "http://localhost:18001/api/v1/overlay/AAPL/headlines?hours=24"
# Wikipedia pageview trend (last 30 days)
curl "http://localhost:18001/api/v1/overlay/AAPL/wiki?days=30"
# FINRA crowding metrics
curl "http://localhost:18001/api/v1/overlay/AAPL/crowding"
# Historical overlay scores (backtesting)
curl "http://localhost:18001/api/v1/overlay/AAPL/history?days=90"
# Manually trigger full pipeline
curl -X POST "http://localhost:18001/api/v1/overlay/admin/trigger-pipeline"</code></pre>
<h2>🔧 Data Sources</h2>
<ul>
<li><strong>SEC EDGAR</strong> - Official company filings and ETF holdings</li>
<li><strong>Yahoo Finance</strong> - Price data, financial news, and RSS headlines</li>
<li><strong>Alpaca</strong> - Market price data (OHLCV) with optional API key</li>
<li><strong>FINRA</strong> - RegSHO short sale volume data (public CDN, no API key)</li>
<li><strong>Wikimedia</strong> - Wikipedia daily page view counts per company</li>
<li><strong>YouTube Data API</strong> - Video mentions from curated investing channels</li>
<li><strong>Google Trends</strong> - Theme interest data (experimental, opt-in)</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": "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)"
}
]
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app.main:app",
host="0.0.0.0",
port=settings.API_PORT,
reload=settings.DEBUG
)