From 1cc5028f783c415936d2358ad7b54d742d34283b Mon Sep 17 00:00:00 2001 From: Rain Ocampo Date: Sun, 23 Aug 2026 06:21:09 +0000 Subject: [PATCH] =?UTF-8?q?Phase=2010a:=20ExecutionContext-Dataclass=20+?= =?UTF-8?q?=20Versionierung=20+=20Tests=20(keine=20Fill-=C3=84nderungen)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system-docs/phase10a_execution_context.md | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 notes/trading/system-docs/phase10a_execution_context.md diff --git a/notes/trading/system-docs/phase10a_execution_context.md b/notes/trading/system-docs/phase10a_execution_context.md new file mode 100644 index 0000000..b71caaf --- /dev/null +++ b/notes/trading/system-docs/phase10a_execution_context.md @@ -0,0 +1,111 @@ +# Phase 10a — ExecutionContext-Dataclass + Versionierung + Tests (M12/M13) + +> Autoren: Rain Ocampo (Hermes) | Datum: 2026-08-23 +> Status: **ABGESCHLOSSEN + DEPLOYT** (Analyse/Design/Verifikation + Datenmodell/Audit/Run-Hash-Integration, OHNE Fill-Änderungen) +> Tags: trading, mt5, architecture, historical-v2, m12, m13, phase10a, execution-context + +--- + +## 1. Ziel (aus Phase-9-Freigabe) + +Phase 10a = **ExecutionContext-Dataclass + Versionierung + Tests**. + +**WICHTIG — NUR Datenmodell/Context/Audit/Run-Hash-Integration vorbereiten.** +Noch KEINE Bid/Ask-Ausführung, KEINE Spread-/Slippage-/Kostenberechnung. +Danach STOPP und Bericht. + +Die 7 festgelegten Entscheidungen (Christian, 22.08.2026) sind als Modellversionen +und Defaults in diesem Modul kodiert — aber es wird NOCH NICHTS berechnet. + +--- + +## 2. Modul: `shared/historical/execution_context.py` (NEU) + +Gemeinsame Quelle für M12 UND M13 (konsistent mit DatasetContext, R1-Prinzip). + +### Enums (festgelegte Bezeichnungen, Entscheidung 7) +- `ExecutionModel`: `LEGACY_SINGLE_PRICE`, `REFERENCE_BID_ASK`, `BROKER_APPROXIMATION`, `BROKER_REPLAY`, `TICK_REPLAY` +- `SpreadModel`: `NONE`, `BID_ASK_INTRINSIC`, `SYNTHETIC_FIXED` (Entscheidung 1) +- `SlippageModel`: `NONE`, `DETERMINISTIC_FIXED` (Entscheidung 3: deterministisch V1) +- `CostModel`: `NONE`, `cost_model_v1_simple` (Entscheidung 4) +- `IntrabarPolicy`: `PESSIMISTIC`(Default), `OPTIMISTIC`, `STOP_FIRST`, `TARGET_FIRST`, `TICK_RESOLUTION`, `UNKNOWN` (Entscheidung 2) + +### Modellversionen (pro Modell unabhängig, künftig bumpbar) +`execution_context_v1`, `execution_model_v1`, `spread_model_v1`, `slippage_model_v1`, `cost_model_v1`, `intrabar_policy_v1`. + +### Dataclass `ExecutionContext` +Felder: `execution_context_version`, `execution_model`+`_version`, `price_basis`, `spread_model`+`_version`+`spread_points`, `slippage_model`+`_version`+`slippage_points`, `cost_model`+`_version`, `intrabar_policy`+`_version`, `feed_type`, `validation_errors`. + +Methoden: `to_dict()`, `is_valid()`, `validate()` (fail-closed bei unbekanntem Modell), Build-Helper `build_execution_context()`. + +**Keine Berechnung** — nur Konfiguration + Modellversionen für Hash/Audit. + +--- + +## 3. service.py-Integration (M12 Run-Hash + Audit) + +### `_compute_run_hash` — erweitert um `execution`-Parameter +- **Legacy** (`execution=None`): exakt alter Hash, **unverändert** (diff-verifiziert). +- **V2** (`execution` gesetzt): fließen zusätzlich ein (Entscheidung J — **Modellwerte + Versionen**): + - `execution_model`, `execution_model_version` + - `price_basis` + - `spread_model`, `spread_model_version` + - `slippage_model`, `slippage_model_version` + - `cost_model`, `cost_model_version` + - `intrabar_policy`, `intrabar_policy_version` + +> **Wichtiger Befund (Reproducibility)**: Anfangs wurden nur die Modell-**Versionen** in den Hash genommen. Das war fehlerhaft: ein Wechsel `bid_ask`→`single` ändert das Ausführungsmodell (REFERENCE_BID_ASK vs LEGACY_SINGLE_PRICE), aber die Version blieb `execution_model_v1` → **identischer Hash**. Korrigiert: **Modellwerte UND Versionen** fließen in den Hash. Test `test_andere_execution_andere_hash` + `test_andere_execution_version_andere_hash` decken beides ab. + +### Gate/Audit — ExecutionContext im V2-Pfad +- Im Gate (`ctx is not None`) wird ein ExecutionContext gebaut (aus `price_basis`+`feed_type` des DatasetContext): + - `price_basis=="bid_ask"` → `execution_model=REFERENCE_BID_ASK` + - sonst → `LEGACY_SINGLE_PRICE` +- Das Execution-Audit (`exec_ctx.to_dict()`) wird **sowohl für allowed als auch blocked Runs** an `audit["execution"]` angehängt (auditierbar). +- Der V2-run_hash nutzt dieses Execution-Audit. + +--- + +## 4. Tests: `m12_app/tests/test_phase10a_execution_context.py` (11/11) + +| # | Test | Zweck | +|---|------|-------| +| 1 | `test_default_legacy_single_price` | Defaults: LEGACY_SINGLE_PRICE, NONE-Modelle, PESSIMISTIC, keine Doppelzählung | +| 2 | `test_build_reference_bid_ask` | REFERENCE_BID_ASK-Ableitung; kein künstlicher Spread | +| 3 | `test_validate_fail_closed_unknown_model` | unbekanntes Modell → invalid | +| 4 | `test_validate_unknown_intrabar` | unbekannte Intrabar-Policy → invalid | +| 5 | `test_to_dict_roundtrip` | to_dict serialisierbar | +| 6 | `test_gleiche_execution_gleicher_hash` | gleiche Execution → gleicher Hash | +| 7 | `test_andere_execution_andere_hash` | bid_ask vs single → anderer Hash + Audit-Model | +| 8 | `test_andere_execution_version_andere_hash` | Versions-Bump → andere Identität | +| 9 | `test_legacy_kein_execution_audit` | Legacy: kein Audit, kein Execution-Dict, Hash unverändert | +| 10 | `test_v2_execution_audit_present` | V2: Audit.execution vorhanden mit Modellversionen | +| 11 | `test_blocked_execution_audit_present` | Blocked-Run trägt Execution-Audit | + +**Regression (alle grün):** +- Phase 8 Gate: 13/13 +- M12 Backtest: 12/12 +- Phase 5: 20/20, Phase 6: 14/14, Phase 7: A–J (Legacy-Hash `8a5760…` unverändert) +- M13 shared A–J + +--- + +## 5. Deploy & Verifikation (Produktion) + +- **Deploy**: `execution_context.py` + `__init__.py` + `service.py` per `docker cp` in **Modul-12-Backtesting**; `execution_context.py` + `__init__.py` auch in **Modul-13-Optimization** (geteiltes shared/historical — sonst ImportError bei `import shared.historical`). +- **chown** auf appuser 1001 (Deploy-Pitfall, bekannt aus Phase 8). +- **Restart** M12 (uvicorn ohne `--reload`). +- **Health**: `/health` 200, `/health/ready` 200 (via Python urllib im Container). +- **Legacy-Smoke PASS**: beide Runs COMPLETED, run_hash `bc6e2553…` + data_hash `d76a7549…` **identisch wie Phase 8** → Legacy unverändert. +- **Rollback-Backup**: `/opt/trading-modules/backup_phase10a_/` (service.py, shared_init.py, execution_context.py). + +--- + +## 6. Nächster Schritt (Phase 10b, nach Freigabe) + +**Bid/Ask-Datenmodell + Fill** — vollständige Propagation der bid/ask-OHLC durch den M12-Engine-/Execution-Pfad (Entscheidung 5), erste echte Bid/Ask-Fills (Long Entry=Ask, Exit=Bid; Short umgekehrt, Entscheidung 1). NOCH NICHT jetzt — STOPP nach dieser Phase. + +--- + +## 7. STOPP + +Phase 10a abgeschlossen. **Keine Implementierung von Phase 10b.** Auf Freigabe warten.