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>
This commit is contained in:
@@ -0,0 +1,204 @@
|
|||||||
|
# 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:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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.
|
||||||
Reference in New Issue
Block a user