Files
Cerbero-Bite/docs/superpowers/specs/2026-05-12-data-quality-audit-design.md
T
Adriano Dal Pastro 374822a931 docs(spec): rimuovi depth_zero_pct + nota su book_depth_top3 NULL-by-design
Allinea la spec all'implementazione: dataclass ChainAuditReport senza
depth_zero_pct, tabella checks senza la riga book_depth_top3=0, output
example senza la riga depth_top3. Aggiunta nota che spiega perché il
check è stato rimosso (colonna popolata solo dal path entry_cycle).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 19:48:23 +00:00

7.0 KiB
Raw Blame History

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 data, 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 data`

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:

@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

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

Nota: il check book_depth_top3 = 0 originariamente previsto è stato rimosso. La colonna è NULL by design sugli snapshot (il collector option_chain_snapshot_cycle non chiama l'orderbook per ogni strike per non saturare l'API Deribit); viene popolata solo dal path entry_cycle per gli strike candidati al picker. Il check di liquidità ha senso solo lì, non in questo audit.

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 data [--db PATH] [--since DAYS] [--json] [--asset ETH|BTC]

Deviazione dal design originale: il group audit era già usato per audit verify (hash chain dell'audit log). Il data-quality audit è stato inserito come sibling audit data per evitare collisione di namespace, anziché come comando top-level cerbero-bite audit.

  • --db PATH (default data/state.sqlite): path al DB SQLite.
  • --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 (DB inesistente, schema mancante).

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%)

=== BTC — ...

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.