Files
Cerbero-mcp/docs/superpowers/specs/2026-04-27-cot-report-design.md
T
AdrianoDev b218ac3a2c
ci / mypy mcp_common (push) Failing after 33s
ci / ruff lint (push) Failing after 37s
ci / pytest (push) Failing after 28s
ci / validate compose + Caddyfile (push) Failing after 33s
ci / build & push to registry (push) Has been skipped
docs: spec design COT report per mcp-macro
TFF (gpe5-46if) per equity/financial: ES, NQ, RTY, ZN, ZB, 6E, 6J, DX
Disaggregated (72hh-3qpy) per commodities: CL, GC, SI, HG, ZW, ZC, ZS

3 tool MCP: get_cot_tff, get_cot_disaggregated,
get_cot_extreme_positioning (scanner percentile 5/95).

Pure-logic helper + httpx integration test + ACL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:43:40 +02:00

7.1 KiB

COT Report — design spec (mcp-macro)

Data: 2026-04-27 Servizio target: mcp-macro Scope: aggiungere supporto al Commitment of Traders (COT) report pubblicato dalla CFTC come fonte di posizionamento istituzionale per opzioni, ETF azionari e materie prime.

1. Motivazione

Il COT settimanale CFTC è uno dei segnali di posizionamento più seguiti per futures sotto la giurisdizione USA (equity, bond, valute, energia, metalli, agricoli). Manca completamente in mcp-macro, che oggi copre solo yields, FRED, calendar, equity futures spot prices.

L'uso primario nel contesto Cerbero:

  • Overlay opzioni Deribit: BTC ha correlazione strutturale con Nasdaq, e il posizionamento dei Leveraged Funds su NQ è un proxy di rischio sistemico equity. Quando i lev funds sono short estremo equity, IV upside premium si comprime → squeeze probabile.
  • Segnali ETF: Asset Manager net (TFF) approssima il flow istituzionale long-only (SPY, QQQ).
  • Materie prime: Producer/Merchant (hedger commerciale) e Managed Money (hedge fund / CTA) sono i veri segnali di top/bottom per oil, gold, copper, agricoli.

2. Decisione: due report, non uno

Approccio adottato:

  • Equity / financial (S&P, NDX, Russell, treasuries, currencies) → TFF (Traders in Financial Futures).
  • Materie prime (oil, gold, silver, copper, grains) → Disaggregated (futures-only & options combined).
  • Legacy (non-commercial vs commercial) → escluso: report obsoleto, troppo aggregato, perde la granularità sui 4 ruoli istituzionali.

Motivazione: i due report coprono i 13 simboli watchlist con la massima granularità senza overlap.

3. Sorgenti dati

API CFTC pubblica (no API key richiesta), endpoint Socrata: https://publicreporting.cftc.gov/resource/<dataset>.json.

Report Dataset ID Frequenza Contenuto
TFF F&O combined gpe5-46if settimanale (ven 15:30 ET) Dealer/Intermediary, Asset Manager, Leveraged Funds, Other Reportables
Disaggregated F&O combined 72hh-3qpy settimanale (ven 15:30 ET) Producer/Merchant, Swap Dealer, Managed Money, Other Reportables

Dati osservati al martedì della settimana, pubblicati il venerdì seguente alle 15:30 ET.

4. Watchlist simboli

TFF

  • ES (E-mini S&P 500)
  • NQ (E-mini Nasdaq-100)
  • RTY (E-mini Russell 2000)
  • ZN (10-Year T-Note)
  • ZB (30-Year T-Bond)
  • 6E (Euro FX)
  • 6J (Japanese Yen)
  • DX (US Dollar Index)

Disaggregated

  • CL (Crude Oil WTI)
  • GC (Gold)
  • SI (Silver)
  • HG (Copper)
  • ZW (Wheat)
  • ZC (Corn)
  • ZS (Soybeans)

Mapping ticker → cftc_contract_market_code mantenuto in costante nel modulo (es. ES → 13874A, CL → 067651). I codici sono pubblici CFTC e non cambiano.

5. Tool MCP esposti

Tre tool, tutti reads (core + observer):

5.1 get_cot_tff(symbol, lookback_weeks=52)

Ritorna serie temporale TFF per un simbolo equity/financial.

Output:

{
  "symbol": "ES",
  "report_type": "tff",
  "rows": [
    {
      "report_date": "2026-04-22",
      "dealer_long": 12345, "dealer_short": 23456, "dealer_net": -11111,
      "asset_mgr_long": 654321, "asset_mgr_short": 200000, "asset_mgr_net": 454321,
      "lev_funds_long": 100000, "lev_funds_short": 350000, "lev_funds_net": -250000,
      "other_long": 50000, "other_short": 50000, "other_net": 0,
      "open_interest": 2500000
    },
    ...
  ],
  "data_timestamp": "2026-04-27T20:00:00Z"
}

5.2 get_cot_disaggregated(symbol, lookback_weeks=52)

Stessa shape, campi diversi: producer_*, swap_*, managed_money_*, other_*.

5.3 get_cot_extreme_positioning(lookback_weeks=156)

Scanner che restituisce, per ogni simbolo della watchlist, il percentile storico (1y o 3y) dell'ultimo net position per il ruolo chiave (Leveraged Funds per TFF, Managed Money per Disaggregated). Flagga estremi a percentili ≤ 5 o ≥ 95.

Output:

{
  "lookback_weeks": 156,
  "extremes": [
    {
      "symbol": "ES", "report_type": "tff",
      "key_role": "lev_funds",
      "current_net": -250000,
      "percentile": 3.2,
      "signal": "extreme_short",
      "report_date": "2026-04-22"
    },
    ...
  ],
  "data_timestamp": "2026-04-27T20:00:00Z"
}

signal{"extreme_short", "extreme_long", "neutral"}.

6. Architettura

mcp-macro/
  src/mcp_macro/
    fetchers.py           # esistente — aggiunge fetch_cot_tff, fetch_cot_disaggregated, fetch_cot_extreme_positioning
    cot_contracts.py      # NUOVO — costanti SYMBOL_TO_CFTC_CODE, CFTC_FIELD_MAPPINGS
    server.py             # esistente — aggiunge 3 endpoint + body models
  tests/
    test_cot.py           # NUOVO — pure-logic test su parsing + percentile + extreme detection
    test_fetchers.py      # esistente — aggiunge integration test con httpx_mock

Logica pura (calcolo percentile, classificazione extreme) in fetchers testata indipendentemente dal layer HTTP. I fetcher async usano mcp_common.http.async_client (retry transport già in place).

7. Cache

  • Chiamata Socrata risponde tipicamente in 200-800ms.
  • COT esce settimanalmente venerdì sera ET → cache TTL 1 ora è eccessiva ma sicura. Riusa il pattern _TREASURY_CACHE esistente in fetchers.py (chiave (symbol, report_type, lookback_weeks)).

8. Edge cases

  • Pre-pubblicazione (es. mercoledì): ultimo report è quello della settimana precedente. Niente da gestire — l'API ritorna l'ultimo disponibile.
  • Simbolo fuori watchlist: get_cot_tff("INVALID") → 400 con payload {"error": "unknown_symbol", "available": [...]}.
  • API CFTC down: retry transport gestisce transient. Su 5xx persistente: ritorna {"rows": [], "error": "cftc_unavailable"}.
  • Lookback troppo corto (< 4 settimane) → percentile inattendibile in extreme positioning. Validation Pydantic: lookback_weeks ≥ 4.

9. Test plan

Pure-logic (no HTTP):

  • compute_percentile(value, history) con casi noti.
  • classify_extreme(percentile, threshold=5) → boundary cases.
  • parse_tff_row() e parse_disaggregated_row() su payload Socrata mock (campi reali documentati).

Integration (httpx_mock):

  • fetch_cot_tff("ES", lookback_weeks=52) con risposta CFTC mock → verifica shape output + ordering rows ASC per data.
  • fetch_cot_extreme_positioning() con dati che includono casi extreme + casi neutral → verifica filtering e signal.

ACL test (TestClient):

  • POST /tools/get_cot_tff con core/observer/no-auth → 200/200/401.
  • POST /tools/get_cot_extreme_positioning idem.

10. Out of scope (versione 1)

  • Storico oltre 3 anni: l'API CFTC ha tutto da 2010, ma lookback default 52w (= 1 anno) e max ragionevole 156w. Storico decennale può essere aggiunto in v2 se serve per backtest.
  • Disaggregated futures-only (dataset diverso da F&O combined): meno usato, skip.
  • Notification al rilascio settimanale: il bot deve schedulare a venerdì 16:00 ET; non è responsabilità del MCP server.
  • Legacy report: escluso (vedi §2).
  • Aggregazione cross-symbol (es. "tutti i metalli combinati"): l'utente compone via tool individuali.