trading-system-docs/notes/trading/system-docs/phase13_4_trust_gate.md

206 lines
7.8 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.

# Phase 13.4 — Historical Data Trust Gate (Provenance + Sanity Validation)
**Status:** FINAL CLOSED (technisch verifiziert, dokumentiert)
**Datum:** 24.08.2026
**Autor:** Rain Ocampo (Hermes)
**Freigabe:** Christian List (Phase 13.4 Finalisierung / Second-Brain-Sync)
---
## 1. Ausgangsproblem (aus Phase 13.3)
Die Phase-13.3-Forensik zeigte: **HTTP 200 + erfolgreiches Parsing bedeutet NICHT automatisch vertrauenswürdige Daten.**
Die bekannten problematischen November-Tage (11-09, 11-10, 11-12) wurden von Dukascopy mit HTTP 200 geliefert, technisch valide geparst, aber enthielten **fachlich unplausible Daten** (falsches Preisniveau, Wochenendaktivität). Der `source_timestamp` war in allen Fällen NULL.
**Kernbefund:** Ein reiner "Download erfolgreich + Parse erfolgreich"-Check ist unzureichend. Es fehlt eine Schutzschicht, die Provenance und fachliche Plausibilität vor der Persistierung validiert.
---
## 2. Neue Pipeline-Reihenfolge
```
RAW DOWNLOAD
→ PROVENANCE CAPTURE
→ TECHNICAL PARSE
→ SANITY VALIDATION
→ TRUST DECISION
→ SESSION
→ TECHNICAL/MARKET QUALITY
→ EFFECTIVE ELIGIBILITY
→ GAP
→ PERSIST RAW
→ DERIVED
→ DATASET VERSION
```
Das Trust Gate sitzt **nach** dem technischen Parse und **vor** der Persistierung (Schritt 4c in `backfill.py`). Es klassifiziert, es löscht nicht.
---
## 3. Provenance-Modell
`HistoricalProvenanceContext` (Version `provenance_v1`) — 16 Felder:
| Feld | Semantik |
|------|----------|
| `provider` | Datenquelle (Dukascopy, OANDA, FXCM, Polygon, …) |
| `instrument` | Instrument (EURUSD, …) |
| `timeframe` | Zeitrahmen (M1, M5, …) |
| `source_url` | Quelle (Credentials redacted) |
| `http_status` | HTTP-Statuscode |
| `content_length` | Rohdatenlänge |
| `raw_file_sha256` | SHA-256 der Rohdatei |
| `retrieved_at` | Abrufzeitpunkt (volatile, nicht im Hash) |
| `source_timestamp` | Quell-Zeitstempel (kritisch, fail-closed) |
| `adapter_version` | Adapter-Version |
| `parser_version` | Parser-Version |
| `normalization_version` | Normalisierungs-Version |
| `code_version` | Code-/Commit-Version |
| `raw_source` | Rohquelle |
| `dataset_identity` | Dataset-Identität |
**Keine Secrets in `source_url`**`redact_url()` entfernt Credentials; `to_dict()` wendet Redaction an.
---
## 4. Trust States
| State | Bedeutung | Persistierbar? |
|-------|-----------|----------------|
| `TRUSTED` | Provenance vollständig, Sanity plausibel | ✅ Ja |
| `CONDITIONAL` | Warnende Hinweise (z. B. Wochenendaktivität) | ⚠️ Nur mit expliziter Policy |
| `UNTRUSTED` | Fachlich unplausible Daten | ❌ Nein |
| `UNKNOWN` | Kritische Provenance fehlt | ❌ Nein (fail-closed) |
Version: `trust_v1`
---
## 5. Reason Codes (20, erweiterbar)
`PRICE_LEVEL_MISMATCH`, `WEEKEND_DATA_ANOMALY`, `STALE_DAY`, `ZERO_VOLUME_DAY`, `SOURCE_TIMESTAMP_MISSING`, `RAW_ARTIFACT_MISSING`, `PROVENANCE_INCOMPLETE`, `CROSS_PROVIDER_MISMATCH`, `NAN_OR_INF`, `INSTRUMENT_MISMATCH`, `TIMEFRAME_MISMATCH`, `TIMESTAMP_DUPLICATE`, `TIMESTAMP_MISSING_MINUTE`, `TIMESTAMP_OUT_OF_RANGE`, `TIMESTAMP_NON_MONOTONIC`, `REFERENCE_UNAVAILABLE`, `PROVIDER_OUTAGE`, `PROVIDER_DATA_ERROR`
---
## 6. Provider-Outage vs Provider-Data-Error
**Strikt getrennte Codepfade:**
- **`PROVIDER_OUTAGE`** — technischer Ausfall: HTTP 404/503, Timeout, Connection-Failure
- **`PROVIDER_DATA_ERROR`** — HTTP 200, technisch valide, aber fachlich unplausible Daten
Diese Zustände bekommen **nie denselben Status/Codepfad**.
---
## 7. Sanity-Prüfungen (6 Klassen, providerneutral)
| Klasse | Prüfung | Konfigurierbar |
|--------|---------|----------------|
| A | Price-Level (gegen unabhängige Referenz) | ✅ asset-class-aware |
| B | Weekend/Session-Anomalie | ✅ |
| C | Stale-Day (flacher Tag) | ✅ |
| D | Volume (Zero-Volume-Day) | ✅ |
| E | Cross-Provider (optional, nur Validierung) | ✅ |
| F | Timestamp (UTC, Monotonie, Duplikate, Range) | ✅ |
**Keine universelle 1,5%-Regel.** Die Forex-Default-Toleranz wurde aus dem Phase-13.3-Befund auf **0,5%** kalibriert (die bekannten falschen Tage wichen nur 0,551,07% ab und wurden bei 1,5% fälschlich TRUSTED). Schwellen sind versioniert und konfigurierbar.
---
## 8. Fail-Closed-Verhalten
- **TRUSTED:** Persistierung zulässig
- **CONDITIONAL:** nur mit expliziter Policy (Audit bleibt erhalten)
- **UNTRUSTED:** nicht als normaler vertrauenswürdiger Historical-Datensatz persistieren
- **UNKNOWN:** fail-closed, sofern kritische Provenance fehlt
---
## 9. Raw-Artifact-Retention
- Content-Hash (SHA-256) + kontrollierter Artifact Store
- Keine unkontrollierte Datenexplosion
- Retention-Policy: Hash + Metadaten (Größe, Retrieval-Zeit, Provider, Instrument, Zeitraum, HTTP-Status, URL ohne Credentials)
---
## 10. source_timestamp-Regel
- **Semantik:** Quell-Zeitstempel des Datenpunkts (UTC)
- **Pflichtgrad:** kritisch
- **Range vs Einzelwert:** beides unterstützt
- **UTC:** zwingend
- **Fail-Closed:** fehlt `source_timestamp``SOURCE_TIMESTAMP_MISSING``UNKNOWN` (nicht persistierbar)
- **Keine Altbestandsmigration** (November-Daten bleiben unverändert)
---
## 11. Hash / Reproduzierbarkeit
- `provenance_hash`: deterministischer SHA-256 über die Provenance
- **Volatile `retrieved_at` wird NICHT in den deterministischen Hash aufgenommen**
- Gleiche Raw-Datei + gleiche Modelle + gleiche Konfiguration → gleicher Hash
- Andere Raw-Datei / andere Trust-Policy → unterscheidbar
---
## 12. Ergebnisse
- **Trust-Gate-Tests: 27/27 grün**
- **Phase-13.3-Reproduction: 9/9 grün**
- 11-09 (Sa): UNTRUSTED (PRICE_LEVEL_MISMATCH + WEEKEND_DATA_ANOMALY)
- 11-10 (So): CONDITIONAL (WEEKEND_DATA_ANOMALY)
- 11-12 (Di): UNTRUSTED (PRICE_LEVEL_MISMATCH)
- **Keiner TRUSTED** — Reason Codes folgen aus den Daten, nicht gefittet
- **Relevante Phase-13-Regressionen grün** (Provider-Failures, Ingestion-State, Idempotenz, Hash-Konsistenz, Daily-Report)
---
## 13. Deploy / Health-Status
- Container `historical-service`: **Up**
- `/health`: `{"status":"ok","service":"historical-data-service","version":"0.1.0-poc"}`
- `/health/ready`: `{"status":"ready","db":"connected"}`
- Trust-Gate-Code im Container importierbar
- `BackfillOrchestrator.run` kennt `trust_gate` (additiv, Standard `None` = Gate inaktiv)
---
## 14. Bekannte Technical Debt (vorbestehend, NICHT Phase-13.4-Regression)
Die folgenden 4 Fehler sind **vorbestehend** — mit dem unveränderten Pre-13.4-`backfill.py` identisch reproduzierbar (definitiv bewiesen):
1. **Aggregation E2E**`TypeError: float - NoneType` (DB-Wahrheit, M5-Aggregation)
2. **Read API E2E**`/bars M5` liefert 24 statt 12 Bars
3. **Parquet POC** — M5-Export/Reimport weicht ab
4. **Test-Fixture-Trennung** — operative Daten enthalten Feb-2024-Testbars
Diese sind **nicht** durch das Trust Gate verursacht und werden **nicht** in Phase 13.4 behoben.
---
## 15. November-Daten
- **Unverändert** (5760 Bars für 11-09 bis 11-12, alle `source_timestamp` NULL)
- **Weiterhin UNTRUSTED** — als forensischer Beweis erhalten
- Nicht gelöscht, nicht überschrieben, nicht reklassifiziert, nicht migriert, nicht repariert
---
## 16. Runner / Watchdog / OANDA / Production Gates
- **Runner p13_eurusd:** PAUSED (unverändert)
- **Watchdog:** unverändert, Recovery-only (Cron `fa8ab4da220f`, DUKASCOPY_RECOVERY ONLY)
- **OANDA:** nicht integriert (keine Credentials, keine API-Calls)
- **Production Gates:** unverändert
- **DB-Schema:** unverändert (additiv, rückwärtskompatibel)
---
## 17. Nächster empfohlener Schritt (NOCH NICHT FREIGEGEBEN)
Ein minimaler kontrollierter **End-to-End-Ingest-Test** soll später beweisen, dass das Trust Gate nicht nur in Unit-/Reproduction-Tests funktioniert, sondern im echten `DOWNLOAD → PROVENANCE → SANITY → TRUST → PERSIST`-Pfad UNTRUSTED/UNKNOWN-Daten tatsächlich VOR der Persistierung blockiert.
**Dieser Test ist jetzt noch NICHT freigegeben.**