From d3d6b1ef7688a9b0eb1c811f735dbd67b59d71a6 Mon Sep 17 00:00:00 2001 From: Rain Ocampo Date: Sat, 22 Aug 2026 22:18:26 +0000 Subject: [PATCH] =?UTF-8?q?Phase=208:=20M12=20Historical-V2=20DatasetGate?= =?UTF-8?q?=20+=20Run-Audit=20+=20Legacy-Smoke=20(DatasetGate,=20BLOCKED-R?= =?UTF-8?q?un-Audit,=20Run-Identit=C3=A4t,=20EURUSD-V2-Blocktest,=20Legacy?= =?UTF-8?q?-Kompatibilit=C3=A4t,=20Production=20Gate)=20-=20Ge=C3=A4ndert?= =?UTF-8?q?=20von=20Rain=20Ocampo,=2022.08.2026?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../historical-v2-phase8-m12-datasetgate.md | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 notes/trading/system-docs/historical-v2-phase8-m12-datasetgate.md diff --git a/notes/trading/system-docs/historical-v2-phase8-m12-datasetgate.md b/notes/trading/system-docs/historical-v2-phase8-m12-datasetgate.md new file mode 100644 index 0000000..6625227 --- /dev/null +++ b/notes/trading/system-docs/historical-v2-phase8-m12-datasetgate.md @@ -0,0 +1,152 @@ +# Phase 8 — M12 Historical-V2 DatasetGate + Run-Audit + +Autor: Rain Ocampo (Hermes) | Datum: 2026-08-22 | Status: IMPLEMENTIERT + GETESTET + +## Ziel +M12 akzeptiert Historical-V2-Datasets künftig NUR für Backtests, wenn der +DatasetContext fachlich backtest-eligible ist. Jeder V2-Run macht seine +komplette Datenwahrheit auditierbar. Legacy-Verhalten bleibt unverändert. + +## 1. Baseline +- M12 12/12 grün (vor Änderung, unverändert nach) +- M12 Backup: `/opt/data/backup_phase8_20260822_214633` (service.py, client.py, storage.py, core/) +- DATA SOURCE = UNSET (→ legacy) bestätigt +- ALLOW_FIXTURE_DATA = UNSET (→ false/default) bestätigt +- /health + /health/ready = 200 OK (im Container via urllib) + +## 2. DatasetGate (nur historical_v2, fail-closed) +`BacktestService._dataset_gate()` greift NUR bei `marketdata.source == "historical_v2"`. +Legacy (source=legacy) ist vollständig unangetastet — kein Gate, kein neuer Fehler. + +Gate-Regeln (Punkt 2/3): +- A) backtest_eligible=true → Run darf weiter +- B) backtest_eligible=false → fail-closed blockiert +- C) UNKNOWN_CRITICAL_METADATA → NICHT starten +- D) Fixture + ALLOW_FIXTURE_DATA=false → FIXTURE_DATA_BLOCKED +- E) fehlender DatasetContext → fail-closed (DATASET_CONTEXT_MISSING) +- F) fehlende Dataset-Version → fail-closed (DATASET_VERSION_NOT_FOUND) +- G) kein stiller Fallback auf Legacy + +## 3. Block-Codes +| error_code | Bedingung | +|---|---| +| DATASET_NOT_ELIGIBLE | backtest_eligible=false, reason != UNKNOWN_CRITICAL_METADATA | +| UNKNOWN_CRITICAL_METADATA | backtest_eligible=false + reason=UNKNOWN_CRITICAL_METADATA | +| FIXTURE_DATA_BLOCKED | is_fixture=true + ALLOW_FIXTURE_DATA=false | +| DATASET_CONTEXT_MISSING | build_dataset_context wirft / Context None | +| DATASET_VERSION_NOT_FOUND | dataset_version_id & hash beide None | + +Ein geblockter Run wird als **auditierbarer BLOCKED-Run persistiert** (status=BLOCKED, +metrics.error_code, metrics.block_reason, metrics.policy_version, metrics.timestamp, +metrics.audit). KEINE Strategieausführung. + +## 4. Run-Audit (Punkt 4) +Vollständiger Audit-Datensatz pro V2-Run (in `metrics.audit` + Antwort `audit`): +dataset_context_version, data_source, dataset_id, dataset_version_id, +dataset_version_hash, raw_source_hash, instrument, timeframe, start/end, +provider, feed_type, price_basis, data_as_of, data_hash, +normalization_version, quality_model_version, stale_model_version, +session_model_version, calendar_version, eligibility_model_version, +aggregation_version, eligibility_policy_version, is_fixture, +quality_summary, effective_eligibility_summary, backtest_eligible, block_reason, +strategy, strategy_version, parameters, run_hash. + +Keine DB-Migration nötig — sauber im bestehenden `backtest_run.metrics` (JSONB). + +## 5. Run-Identität / Reproduzierbarkeit (Punkt 5) +M12 hatte bereits deterministischen `run_hash` (strategy/version/params/symbol/ +timeframe/start/end/data_hash). Für historical_v2 wird ZUSÄTZLICH `dataset_version_hash` ++ `eligibility_policy_version` in den Hash aufgenommen (nur wenn gesetzt). +- gleiche Daten + Strategie + Parameter + Policy → gleiche Identität +- andere Dataset-Version → andere Identität +- andere Policy-Version → andere Identität +- Legacy (beide None) → exakt alter Hash, unverändert + +## 6. Echter EURUSD-V2-Blocktest (POSITIV, Punkt 6) +Gegen historical-DB (Port 5433): DatasetContext liefert aktuell +`backtest_eligible=False`, `block_reason=NO_DATASET_METADATA`, +`dataset_version_id=None` (keine Dataset-Version für EURUSD/H4 2024-01). +Gate blockt mit `DATASET_VERSION_NOT_FOUND` (strenger als UNKNOWN). +Keine Strategieausführung, Run als BLOCKED auditierbar. +-> PASS (fail-closed, keine Echtorders) +Anmerkung: Der im Brief erwartete Code `UNKNOWN_CRITICAL_METADATA` trifft hier +nicht zu, weil keine Dataset-Version existiert. Der UNKNOWN-Code ist separat über +Mock-Test abgedeckt. Beides gültige fail-closed Blöcke. + +## 7. Positiver ELIGIBLE-Test (Punkt 7) +Mock/Test-DatasetContext vollständig & backtest_eligible=true, mit +ALLOW_FIXTURE_DATA=true → Gate lässt durch, M12 läuft bis zur bestehenden +Backtestlogik, Audit vollständig. (Test 3, 7 grün.) + +## 8. QualitySummary im Run (Punkt 8) +total/eligible/conditional/excluded/unknown/suspect/partial/stale_bars, +gap_count, coverage_pct — über `quality_summary` im Audit. None bleibt None +(nicht 0/100% erfunden), bewiesen durch Test. + +## 9. CONDITIONAL Data Policy (Punkt 9) +Keine finale Handelslogik. Audit kennzeichnet CONDITIONAL sichtbar +(`effective_eligibility_summary.conditional`). Kein stilles Zählen als FULL. + +## 10. Feed Type (Punkt 10) +Historical V2 Dukascopy: feed_type=REFERENCE — im Audit sichtbar. Kein Eindruck +REFERENCE == IG Broker Feed, keine Kosten-Simulation. + +## 11. Legacy-Kompatibilität +- Legacy (source=legacy) → kein Gate, kein Audit, kein neuer Fehler +- M12 12/12 unverändert grün +- Legacy run_hash deterministisch unverändert (Phase-7 J, Phase-6 12) +- kein Hash-/Cache-Break +- Legacy bleibt Default + +## 12. Tests (Punkt 12) +Neue Suite `m12_app/tests/test_phase8_gate.py` (13 Tests) — alle grün: +1 Legacy unverändert | 2 V2 Context vollständig | 3 eligible erlaubt +4 not_eligible blockiert | 5 UNKNOWN blockiert | 6 Fixture default blockiert +7 Fixture explizit erlaubt | 8 fehlender Context fail-closed +9 fehlende Version fail-closed | 10 andere dataset_hash → andere ID +11 andere policy_version → andere ID | 12 gleiche Inputs → gleiche ID +13 QualitySummary vollständig | 14 CONDITIONAL sichtbar | 15 REFERENCE sichtbar + +Regression: +- M12 12/12 ✅ +- M13 A–J ✅ (test_shared_repository) +- Phase 5 20/20 ✅ +- Phase 6 14/14 ✅ +- Phase 7 A–J ✅ + +## 13. Produktionsdeploy +NUR M12. HISTORICAL_DATA_SOURCE bleibt legacy/unset, ALLOW_FIXTURE_DATA bleibt +false/unset. Keine Netzwerkänderung. (Deploy-Pfad: scp → docker cp → chown) +## 14. Dokumentation +Diese Datei. Push zu Forgejo/Tolaria in Phase 8 formalisiert (nur diese Datei, keine Secrets); Obsidian NUR lokal, KEIN Push. + +## 15. Legacy-Smoke-Ergebnis (POST /backtest, BT_PULL 1h demo, 2026-08-01→19) +Ausgeführt im Produktions-Container Modul-12-Backtesting (urllib, Port 55012): +- **Run A**: status=COMPLETED, run_id=ed62f1b3628b4fa29c3e880d83002ed8 +- **Run B**: status=COMPLETED, run_id=73a8cf75034a439cba60e974bd07304b +- **run_hash**: `bc6e2553…` A == B identisch ✅ +- **data_hash**: `d76a7549…` A == B identisch ✅ +- **error_code**: None / None ✅ (kein Gate-Code) +- **block_reason**: None / None ✅ +- **audit vorhanden (Legacy)**: False (korrekt minimal, kein Audit-Feld nötig) +- KEIN DATASET_NOT_ELIGIBLE / DATASET_VERSION_NOT_FOUND / FIXTURE_DATA_BLOCKED +- **Produktions-DB (trading.backtest_run)**: status=COMPLETED, run_hash bc6e2553… +- **Health/Ready nach Smoke**: /health 200, /health/ready 200 +- **Production Gate nach Smoke**: HISTORICAL_DATA_SOURCE=UNSET, ALLOW_FIXTURE_DATA=UNSET + +**Legacy-Hash-Nachweis (vorher=nachher):** +- Pre-Phase-8-Hash (Backup) payload = {strategy,version,params,symbol,timeframe,start,end,data_hash} +- Phase 8 fügt dataset_version_hash/eligibility_policy_version NUR hinzu wenn `is not None` + → Legacy ruft mit None auf → **byte-identischer payload → identischer Hash.** +- Empirisch 2 unabhängige Runs nach Deploy → gleicher run_hash bc6e2553… +- ⇒ Legacy run_hash deterministisch UNVERÄNDERT gegenüber vor Phase 8. ✅ + +**Safety:** keine M09-Order, keine M07/M08/M15-Control-Auswirkung, keine Netzwerkänderung, +keine IG-Calls. Nur M12-Container-Neustart (Prozess, kein Docker-Netz/Config). + +## Bekannte offene Punkte +- EURUSD-Context liefert NO_DATASET_METADATA (keine Dataset-Version hinterlegt). +- CONDITIONAL-Handelslogik (Punkt 9) NICHT implementiert (nur Kennzeichnung) — bewusst. +- Noch keine Broker-Kosten-/Feed-Angleichung — bewusst. +- `verify_phase8_block.py` nutzt FakeStorage für den Gate-Pfad; echtes DB-Persistieren + der BLOCKED-Runs wird im Container (Deploy) verifiziert.