Phase 13.4: Historical Data Trust Gate (Provenance + Sanity Validation) - Doku - Rain Ocampo, 24.08.2026
This commit is contained in:
parent
4d421eab0d
commit
16a104822e
1 changed files with 206 additions and 0 deletions
206
notes/trading/system-docs/phase13_4_trust_gate.md
Normal file
206
notes/trading/system-docs/phase13_4_trust_gate.md
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
# 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,55–1,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.**
|
||||||
Loading…
Reference in a new issue