The disagg dataset (2019–2026, 468 markets) was in the DB but invisible.
This wires it into every layer of the app:
Backend:
- models.py: add has_disagg to CommodityMeta; add DisaggPositionPoint,
DisaggHistoryResponse, DisaggScreenerRow models
- commodities.py: join disagg_reports to populate has_disagg flag and
correct first/last dates; HAVING filter removes markets with no data
- disagg.py (new): /api/disagg/{code}/history, /api/disagg/screener,
/api/disagg/{code}/net-position-percentile, /api/disagg/compare
- main.py: register disagg router
Frontend:
- Metric selector shows Disaggregated optgroup (Managed Money, Prod/Merchant,
Swap Dealer, Other Rept) when market has has_disagg=true, hides Legacy group
- Detail view auto-switches to disagg endpoint and defaults to m_money_net
for disagg markets; shows green "Disaggregated" badge
- Screener always uses disagg endpoint (Managed Money percentile rank)
- Compare uses /api/disagg/compare for disagg metrics
- style.css: add .badge-disagg green variant
Result: wheat markets (SRW, HRW, HRSpring, Black Sea) now show 7 years of
disaggregated positioning data with Managed Money as the default metric.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
40 lines
1.2 KiB
Python
40 lines
1.2 KiB
Python
from fastapi import FastAPI
|
|
from fastapi.responses import FileResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from pathlib import Path
|
|
|
|
from app.api.routes import commodities, positions, analytics, reports, disagg
|
|
|
|
app = FastAPI(
|
|
title="CFTC COT Explorer",
|
|
description="Explore CFTC Commitments of Traders positioning data",
|
|
version="1.0.0",
|
|
)
|
|
|
|
app.include_router(commodities.router)
|
|
app.include_router(positions.router)
|
|
app.include_router(analytics.router)
|
|
app.include_router(reports.router)
|
|
app.include_router(disagg.router)
|
|
|
|
FRONTEND_DIR = Path(__file__).parent.parent.parent / "frontend"
|
|
|
|
if FRONTEND_DIR.exists():
|
|
app.mount("/static", StaticFiles(directory=str(FRONTEND_DIR)), name="static")
|
|
|
|
|
|
@app.get("/", include_in_schema=False)
|
|
async def root():
|
|
index = FRONTEND_DIR / "index.html"
|
|
if index.exists():
|
|
return FileResponse(str(index))
|
|
return {"message": "CFTC COT Explorer API", "docs": "/docs"}
|
|
|
|
|
|
@app.get("/health", include_in_schema=False)
|
|
async def health():
|
|
from app.db import get_db
|
|
with get_db() as conn:
|
|
count = conn.execute("SELECT COUNT(*) FROM reports").fetchone()[0]
|
|
return {"status": "ok", "reports": count}
|