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>
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_CACHEesistente infetchers.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()eparse_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_tffcon core/observer/no-auth → 200/200/401.POST /tools/get_cot_extreme_positioningidem.
10. Out of scope (versione 1)
- Storico oltre 3 anni: l'API CFTC ha tutto da 2010, ma
lookbackdefault 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.