Greg 90c2ae3f35 Expose disaggregated COT data in the UI (wheat focus)
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>
2026-03-22 18:22:16 +01:00
..