Files
Cerbero-Bite/docs/superpowers/specs/2026-05-12-data-quality-audit-design.md
T
root 569df334dc docs(spec): data quality audit design (chain + market_snapshots)
Spec del comando CLI `cerbero-bite audit`: copertura temporale, gap,
fetch_ok streaks, NULL rate per market_snapshots; snap mancanti,
quote/snap, bid>ask, IV null, depth zero per option_chain_snapshots.
Output stdout + opzionale --json. Pre-requisito al backtest
non-stilizzato.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:51:51 +00:00

189 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Data Quality Audit — Design Spec
**Date:** 2026-05-12
**Status:** Approved (design phase)
**Author:** session-driven (operator + agent)
## Motivation
Prima di costruire il backtest non-stilizzato su `option_chain_snapshots`
(prossimo macro-step del progetto), serve confermare che i dati
raccolti negli ultimi 11 giorni (ETH) e 8 giorni (BTC) siano usabili:
copertura temporale piena, niente buchi sistemici, niente quote
malformate (bid > ask, IV mancante, depth book a zero). Lo stesso
audit dev'essere ri-eseguibile periodicamente come check operativo.
`market_snapshots` rientra nello scope per simmetria (entrambe le
tabelle alimentano la decisione di entrata e il monitoring), mentre
`dvol_history` è escluso: appena migrato a multi-asset (commit
`19695e4`), serie troppo corta per BTC (29 righe al momento del
design) e copertura ETH già implicita in `market_snapshots`.
## Scope
**In scope:**
- `market_snapshots`: continuità temporale, fetch_ok streaks, NULL rate
per colonna numerica, parità ETH/BTC.
- `option_chain_snapshots`: snapshot mancanti, distribuzione quote per
snap, bid/ask sanity, IV null rate, book depth.
- CLI subcommand `cerbero-bite audit`, output stdout + opzionale `--json`.
**Out of scope:**
- `dvol_history`, `decisions`, `positions`, `instructions`,
`manual_actions` (non rilevanti per il backtest non-stilizzato).
- Audit di consistenza cross-tabella (es: per ogni snapshot chain esiste
uno snapshot market) — interessante ma rinviato.
- Persistenza dei risultati audit nello stesso DB.
## Architecture
```
src/cerbero_bite/analysis/
__init__.py
data_audit.py # logica pura, no I/O lato MCP
src/cerbero_bite/cli.py # nuovo subcommand `audit`
tests/unit/
test_data_audit.py # DB temporaneo + seed deterministico
```
`data_audit.py` espone funzioni pure che prendono una `sqlite3.Connection`
e una finestra temporale, ritornano `dataclass` di risultati. Il CLI
apre la connection in read-only, chiama le funzioni, formatta l'output.
Funzioni principali:
```python
@dataclass(frozen=True)
class MarketAuditReport:
asset: str
expected_ticks: int
actual_ticks: int
coverage_pct: Decimal
gaps_over_threshold: list[GapRecord]
fetch_ok_zero_count: int
max_fetch_ok_zero_streak: int
null_rate_by_column: dict[str, Decimal]
@dataclass(frozen=True)
class ChainAuditReport:
asset: str
expected_snapshots: int
actual_snapshots: int
coverage_pct: Decimal
quotes_per_snap_median: int
quotes_per_snap_p10: int
quotes_per_snap_p90: int
bid_gt_ask_count: int
iv_null_count: int
iv_null_pct: Decimal
depth_zero_pct: Decimal
def audit_market_snapshots(conn, *, asset, since, now) -> MarketAuditReport: ...
def audit_option_chain(conn, *, asset, since, now) -> ChainAuditReport: ...
```
## Checks & Thresholds
| Tabella | Check | Soglia "bad" | Rationale |
|---|---|---|---|
| market_snapshots | gap tra tick consecutivi | > 20 min | cron è `*/15`; +5 min tolleranza |
| market_snapshots | streak `fetch_ok=0` | ≥ 3 consecutivi | 1-2 = transient MCP, 3+ = pattern |
| market_snapshots | NULL rate per colonna | > 10% nella finestra | una metrica con >10% NULL non è affidabile per backtest |
| option_chain_snapshots | snap mancanti | qualsiasi (count visibile) | cron `*/15`, ogni miss è significativo |
| option_chain_snapshots | quote/snap < 50% mediana 24h | qualsiasi | rilevatore di chain truncate (mismatch with width filter) |
| option_chain_snapshots | bid > ask | qualsiasi | dato corrotto, da indagare |
| option_chain_snapshots | IV null/non-parseable | conteggio + % | IV è chiave per BS skew calibration |
| option_chain_snapshots | `book_depth_top3 = 0` | % per snapshot | proxy di illiquidità |
Le soglie sono costanti modulo (non config YAML) per ridurre il blast
radius dei cambi: il backtest e l'audit girano in contesti diversi,
non condividono parametri operativi.
## CLI
```
cerbero-bite audit [--since DAYS] [--json] [--asset ETH|BTC]
```
- `--since DAYS` (default `7`): finestra di analisi, retro dal `now()` corrente.
- `--json` (default off): stampa solo dump JSON serializzabile, niente tabelle umane.
- `--asset` (default `tutti`): filtra ad un singolo asset.
Exit code:
- `0`: audit completato (a prescindere dai problemi trovati).
- `2`: errori di connessione/DB (separare da problemi nei dati).
Niente exit code per "found issues": l'audit è informativo, decide
l'umano. Far diventare l'audit un gate CI è out of scope.
## Output
**Stdout (default):**
```
=== ETH — market_snapshots (last 7d, 2026-05-05 → 2026-05-12) ===
ticks: 672 expected: 672 coverage: 100.0%
gaps > 20min: 0
fetch_ok=0: 4 rows (max streak: 1)
null rate: dealer_net_gamma 2.1% oi_delta_pct_4h 0.3%
=== ETH — option_chain_snapshots (last 7d) ===
snapshots: 672 expected: 672 coverage: 100.0%
quotes/snap: median 55 p10 50 p90 60
bid > ask: 0
IV null: 12 quotes (0.03%)
depth_top3 = 0: 1.2% of quotes
=== BTC — ...
```
**JSON (`--json`):**
```json
{
"since": "2026-05-05T20:46:00+00:00",
"until": "2026-05-12T20:46:00+00:00",
"assets": {
"ETH": {
"market": {"expected_ticks": 672, "actual_ticks": 672, ...},
"chain": {"expected_snapshots": 672, ...}
},
"BTC": {...}
}
}
```
## Testing
`tests/unit/test_data_audit.py`. Per ogni funzione:
- DB temporaneo (`tmp_path`), schema migrato via `run_migrations`.
- Seed deterministico: insert manuali per riprodurre lo scenario.
- Test cases:
- market: copertura piena → 100%; un gap iniettato → conteggio gap=1;
streak `fetch_ok=0` lunga 3 → flagged.
- chain: snap mancante → expected actual = 1; quote dimezzate in
un tick → quotes/snap p10 cala; `bid=10 ask=5` → bid>ask=1.
- 0 dipendenze nuove (sqlite + pytest standard).
## Performance
Tabelle attuali: ~57k quote chain. Le query usano gli index
`idx_option_chain_asset_ts` e `(asset, timestamp)` di
`market_snapshots`. L'audit deve girare in < 2s su 7gg.
## Anti-goals (esplicito)
- Nessun salvataggio dei risultati nello stato del DB.
- Nessun trigger automatico (no cron job, no APScheduler).
- Nessun alert/notifica: stdout + JSON sono lo strumento, l'operatore
decide cosa farne.
- Nessun ML / detection di anomalie sofisticate. Soglie costanti.
## Open Questions
Nessuna al momento della scrittura. Eventuali punti emergeranno durante
l'implementazione e andranno annotati qui.