C3F FINAL AUTO_SAFE BATCH 3: migriert alle 21 verbleibenden AUTO_SAFE Knowledge Objects auf knowledge_schema v1 (18 standalone logs + 3 standalone-canonical). Body byte-genau, IDs stabil (0 Kollisionen). Keine HR/DO_NOT_TOUCH berührt. C3 MIGRATION weiter IN_PROGRESS.
160 lines
7.9 KiB
Markdown
160 lines
7.9 KiB
Markdown
---
|
||
id: object/ac1df780-019b-bb11-cb91-4edaa49e1843
|
||
type: arch
|
||
role: history
|
||
representation: canonical
|
||
state: historical
|
||
knowledge_schema: 1
|
||
---
|
||
# 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.
|