Phase 9: M12/M13 Phase-2-Readiness-Audit + Implementierungsmatrix A-N (Analyse/Design/Verifikation, keine Implementierung)

This commit is contained in:
Rain Ocampo 2026-08-22 23:20:21 +00:00
parent d3d6b1ef76
commit e8d91b78b3

View file

@ -0,0 +1,341 @@
---
type: Note
tags: [trading, mt5, architecture, historical-v2, m12, m13, phase9, readiness-audit, phase2]
created: 2026-08-22
---
# Phase 9 — M12/M13 Phase-2-Readiness-Audit + Implementierungsmatrix AN
> Autor: Rain Ocampo | Datum: 22.08.2026 | Status: ✅ ANALYSE + DESIGN + VERIFIKATION (KEINE Implementierung)
> **Geändert von: Rain Ocampo, Datum: 22.08.2026, Grund: Phase-9-Audit (read-only, Code-Evidenz)**
## Executive Summary
Phase 2 (realistischer Backtest: Bid/Ask-Fill, Spread, Slippage, Kosten, Intrabar, Gap) ist NICHT implementiert. Das heutige M12 nutzt ein **reines Single-Price-Bid-Modell** (nur Legacy-`ohlcv`-`close`, oder V2-`bid_close` als `close`). Es gibt keine Bid/Ask-Fill-Logik, kein separates Spread-Modell außer einem pauschalen Preispunkte-Abschlag auf Entry+Exit, keine Slippage außer derselben Punkte-Verschlechterung, und keinen Kosten-Breakdown. Der wichtigste Verzerrungs-Punkt: **Der Intrabar-Exit ist symmetrie-verzerrt** — Stop gewinnt immer bei Stop+Target-in-einer-Bar (konservativ, aber pessimistisch für Target-Trades).
Die Erweiterung muss einen **ExecutionContext** einführen, der `execution_model`, `spread_model`, `slippage_model`, `cost_model`, `intrabar_policy`, `price_basis`, `feed_type` transportiert und versioniert — inklusive Run-Hash-Integration, aber **strikt getrennt** von Dataset-Identität und Strategie-Parameter.
---
## A — PRICE MODEL / SINGLE-PRICE AUDIT
### Candle-Datenmodell (M12)
- **Legacy** (`repository.py:_load_candles_legacy`, Zeile 305-327): `ohlcv`-Tabelle, Keys `timestamp, open, high, low, close, volume`. **Nur Einzelpreis** pro Bar (Mid/Einzelpreis). Kein bid/ask.
- **V2** (`repository.py:_load_candles_v2`, Zeile 332-371): liest aus `historical_bar` und mappt **`bid_open→open, bid_high→high, bid_low→low, bid_close→close`**. Das `historical_bar`-Schema HAT bid_open..ask_close (verifiziert: 37.480/37.480 bid+ask, mid=0), aber `load_candles` **transportiert sie NICHT** — wirft die ask-Seite weg und liefert nur bid-OHLC.
- Die Engine bekommt also immer OHLC (nur bid/Einzel) — **niemals ask**.
### Entry / Exit / Stop / Target (engine.py)
| Feld | Quelle | Zeile |
|---|---|---|
| Entry (Signal) | `setup.entry` = **letzter Close der Signal-Candle** (Strategie: `entry = last_close`) | strategy 134/175 |
| Entry-Fill (Open Pos) | `next_candle["open"]` + Spread + Slippage (verschlechtert) | `_open_position` 192-214 |
| Stop | `setup.stop_loss` = Swing-Low/High ± buffer | 135, 176 |
| Target | `setup.target` = Entry ± 2R | 139, 180 |
| Exit (normales Ende) | `candle["close"]` (OHNE Slippage/Spread auf Stop/Target) | `_close_position` 283-336 |
| Market-Order-Fill | pauschaler Preispunkte-Abschlag LONG+ / SHORT- | 198-200 |
| Gap | Open-Fill zum Gap-Open | `_evaluate_exit` 268-280 |
| Run-Ende | Force-Close zum letzten `close` | 141-153 |
### Aktuelle Annahmen (Single-Price)
| Annahme | Konsequenz |
|---|---|
| `open`/`close` sind handelbarer Preis (Einzel) | Kein Bid/Ask-Spread |
| LONG Entry = OPEN + spread + slippage | Wird besser (künstlicher Overhead) |
| LONG Exit = CLOSE (bei normalem Exit) | Kein Exit-Spread/Slippage — nur Entry hat Kosten |
**RISIKO**: V2-Daten sind real `bid_ask`, aber `load_candles` reduziert auf bid — die ask-Seite wird VOR der Engine verworfen. Realisierung als Bid/Ask-Fill ist daher **nicht möglich ohne Datenmodell-Änderung**.
**SOLL**: Engine muss `price_basis` + `bid_*`/`ask_*`/`mid_*` transportieren; Entry/Exit-Getrennt je nach Richtung; Stop/Target auf entsprechende Seite anwenden.
---
## B — BID/ASK READINESS
### Was liefert Historical V2 tatsächlich?
- `historical_bar` hat **`bid_open/high/low/close` UND `ask_open/high/low/close`** (verifiziert: 37.480/37.480/37.480, mid=0) — bid_ask vorhanden, mid NICHT.
- `load_bars_with_quality` (Zeile 163-247) transportiert bid+ask+mid+quality-Keys (vollständig).
- ABER: `load_candles` (der Pfad, den die Engine nutzt) liefert **nur bid-OHLC** — ask wird verworfen.
### Kann M12 es heute transportieren?
**Nein, nicht über den Engine-Pfad.** Die Engine bekommt `open/high/low/close` als Einzelwerte. `build_dataset_context` bringt `price_basis="bid_ask"` (aus Bar-Metadaten), aber diese Info erreicht die Engine-Fills **nicht** (nur Run-Audit).
### Zielmodell
```
LONG: Entry grundsätzlich Ask (Käufer zahlt ask)
Exit grundsätzlich Bid (Verkäufer erhält bid)
SHORT: Entry grundsätzlich Bid (Verkäufer verkauft bid)
Exit grundsätzlich Ask (Käufer erhält ask)
```
**Sonderfälle (zu dokumentieren, NICHT implementieren):**
- **Stop LONG**: Löse aus, wenn bid_low ≤ stop → fill = stop (oder gap-open bid, wenn gap unter)
- **Stop SHORT**: bid_high ≥ stop → fill = stop (bid/ask?)
- **Target LONG**: ask_high ≥ target → fill = target
- **Market**: Entry LONG = ask_open; Entry SHORT = bid_open
- **Limit**: (Phase 2 nicht eingeführt — kein Limit-Fill)
- **Gap**: bid/ask-gap-opening überschlägt Stop/Target
- **Bar Open**: Entry zum nächsten Bar-Open auf der korrekten Seite
- **End-of-data**: force-close zum letzten bid/ask-close
---
## C — SPREAD AUDIT
Heute (**engine.py** `_open_position` Zeile 198-201, `_close_position` Zeile 297-303):
- **Spread = ein fester Punktwert** (`params["spread"]`, Default 0.0, M13-Default 0.0).
- **Einheit: Preispunkte** (absolute Preisdifferenz, nicht %).
- Anwendung: **sowohl Entry als auch Exit**, je Richtung versetzt — d.h. ein **doppelter Spread pro Trade** (Entry+Exit) statt einmal.
- **Preisbasis**: unklar — es wird einfach von `open`/`close` abgezogen, ohne bid/ask-Bezug. Da Daten heute bid-single sind, ist es ein "Abschlag" auf den einzigen Preis.
**Doppelzählungsrisiko**: Wenn Bid/Ask-Fill eingeführt wird, ist der aktuelle pauschale Spread-Abzug bereits implizit im echten bid/ask-Spread enthalten. Zusätzlich ein künstlicher `spread`-Abzug ⇒ **Doppelzählung**.
**Zielregel**:
- `price_basis=bid_ask` (echte bid/ask) → Spread NICHT künstlich aufschlagen; nutze den intrinsischen bid/ask.
- `price_basis=mid`/single → synthetisches Spread-Modell möglich (aufzuschlagen).
- Beide Modi **strikt unterscheidbar** durch `spread_model` (z.B. `BID_ASK_INTRINSIC` vs `SYNTHETIC_FIXED` vs `SYNTHETIC_POINTS`).
---
## D — SLIPPAGE AUDIT
Heute (engine.py):
- **Wann**: nur Entry + normaler Exit (nicht Stop/Target, siehe unten).
- **Einheit**: Preispunkte (`params["slippage"]`, Default 0.0).
- **Long/Short**: symmetrisch — Long schlägt auf (Entry+Exit), Short zieht ab. Adversarial in beiden Fällen.
- **Deterministisch**: JA — ein fester Punkte-Wert. Kein Zufall, kein Seed.
- **Bestandteil run_hash**: JA (steht in `params_snapshot``_compute_run_hash` inkludiert `params`). Slippage-Änderung ändert run_hash.
- **Bestandteil Audit**: indirekt über `parameters` (in `metrics.audit` nicht explizit).
**Zielmodell**: `slippage_model` (`DETERMINISTIC_FIXED` | `SYNTHETIC_POINTS` | `BROKER_REPLAY`...). Kein Zufall im deterministischen Kern; falls stochastisch, Seed fix + in Hash. Jeder Trade trennbar slippage_cost.
---
## E — COST MODEL
| Kosten | Status | Quelle | Heutige Modellierung |
|---|---|---|---|
| **Commission** | TEILWEISE | CONFIG (`fee_fixed`+`fee_pct`) | `_fee()` auf Entry+Exit |
| **Spread** | TEILWEISE | CONFIG (`spread`, Punkte) | pauschaler Preispunkte-Abschlag auf Entry+Exit |
| **Slippage** | TEILWEISE | CONFIG (`slippage`, Punkte) | pauschaler Preispunkte-Abschlag auf Entry+Exit |
| **Financing/Overnight/Swap** | NICHT | — | nicht modelliert |
| **FX conversion** | NICHT | — | nicht modelliert |
| **Guaranteed Stop Premium** | NICHT | — | nicht modelliert |
| **sonstige Gebühren** | NICHT | — | nicht modelliert |
**Kosten-Quellen heute**: ausschließlich **CONFIG** (keine Broker-, keine Referenz-Kosten). Keine IG-Gebühren erfunden ✓.
**Doppelzählungsrisiko (Kosten)**: `fee` UND `spread` UND `slippage` werden alle separat abgezogen — kein Broker/Referenz-Double-count, aber `spread` als pauschaler Abzug + künftiges bid/ask würde doppelt zählen (siehe C).
---
## F — INTRABAR AMBIGUITY (engine.py `_evaluate_exit`, Zeile 251-281)
**Aktuelles Verhalten — Code-Evidenz:**
- **LONG**: Stop wird VOR Target geprüft. Wenn `open<=stop`→Gap-Stop. Dann `if low<=stop`→Stop. Dann `if high>=target`→Target.
**Wenn Stop UND Target in derselben Bar****Stop gewinnt IMMER** (Worst-Case konservativ).
- **SHORT**: spiegelbildlich (Stop zuerst).
**Verzerrung**: M1-OHLC kann reale Intrabar-Reihenfolge nicht bestimmen (bekannt aus POC). Aktuelle Wahl ist deterministisch-pessimistisch (Stop-first), aber **kann Target-Gewinner in realistischen Sequenzen unterschätzen** (Stop-first bei bar mit beiden → Trades als Verluste klassifiziert, die real oft Target-Trades wären).
**Zielmodi (Design, NICHT implementiert)**:
- `PESSIMISTIC` (= Stop-first, heutiges Verhalten)
- `OPTIMISTIC` (= Target-first)
- `STOP_FIRST` / `TARGET_FIRST` (explizite Varianten)
- `TICK_RESOLUTION` (benötigt Tick-Daten — nur V2 nicht lieferbar)
- `UNKNOWN/BLOCK` (fail-closed: keine Intrabar-Reihenfolge festgelegt → Run blocken)
- Optional `DEFAULT=STOP_FIRST` (behält heutiges Verhalten als Baseline; künftig wählbar)
---
## G — GAP EXECUTION (engine.py, Zeile 268-280)
- **LONG Stop-Gap**: wenn `open<=stop` → Fill zum `min(open, stop)` = Gap-Open (schlechter). Bewiesen: ein Gap-Down unter Stop füllt nicht perfekt bei stop, sondern Gap-Open.
- **LONG Target**: `high>=target`→ fill=target (kein Gap-Fall bei Target gesondert). wenn `open>=target` → wird als `high>=target` abgefangen und zum target gefüllt (das wäre Optimistisch — Gap UP über Target müsste evtl. Gap-Open, nicht target).
- **SHORT analog**.
- **RISIKO**: `target`-Fill bei Gap wird zum target (nicht gap-open) — unrealistisch optimistisch für Gap-UP-LONG (sollte bid/ask-open nach gap). Umgekehrt ist der Stop-Gap korrekt pessimistisch.
- **Zielverhalten**: Gap-Fill-Regel konsistent: bei Gap-Öffnung jenseits Stop/Target → zum **Gap-Open der realen ausführbaren Seite**, nie zum anvisierten Level.
---
## H — REFERENCE VS BROKER FEED
- **Heute**: M12 kennt keinen Feed-Unterschied. Engine ignoriert `feed_type`/`price_basis`. Ergebnis wird als generischer Backtest-Output geliefert (einzig Run-Audit enthält `feed_type`/`price_basis`, aber nur bei V2 + Context).
- **Zielregel**: REFERENCE-Daten nie als exakte Broker-Ausführung ausgeben. Run muss `execution_model` + `cost_model` + `spread_model` + `feed_type` + `price_basis` sichtbar kennzeichnen.
- **Execution-Klassen (Phase 2)**:
- `MARKET_SIMULATION` — nur OHLC, synthetische Modelle (Spread/Slippage/Cost als config)
- `BROKER_APPROXIMATION` — echte bid/ask OHLC (REFERENCE), realistische Spread/Fill
- `BROKER_REPLAY` — echte Broker-Feed + Broker-Fill (IG later), fehler bei REFERENCE
- `BROKER_REPLAY` auf REFERENCE-Daten → **fail-closed** (inconsist).
---
## I — M13 OPTIMIZATION IMPACT
M13 (optimizer.py) ruft M12 mit `spread`/`slippage`/`fee_fixed`/`fee_pct` als **feste Baustein-Argumente** (`_bt_kwargs`, Zeile 326-335, alle Default 0.0). **NICHT Teil des Optimizer-Suchraums**`param_space` enthält nur Strategie-Parameter.
**Gefahr**: Wenn `spread`/`slippage`/Fees in `param_space` aufgenommen würden, könnte der Optimizer sie MINIMIEREN (um Score zu maximieren) — realistische Kosten würden "schönoptimiert". Das ist **verboten**.
**Klassifikation (Phase-2-Design)**:
| Klasse | Beispiele | M13 optimiert? |
|---|---|---|
| STRATEGY_PARAMETER | ema_period, rr_multiplier, stop_buffer | JA |
| EXECUTION_PARAMETER | intrabar_policy, gap_rule | NEIN |
| BROKER_PARAMETER | spread, slippage, commission, financing | NEIN |
| DATA_PARAMETER | data_source, feed, price_basis, timeframe | NEIN |
| POLICY_PARAMETER | eligibility_policy, quality_rule | NEIN |
M13 darf **standardmäßig nur STRATEGY_PARAMETER** optimieren; alle anderen Klassen bleiben fix aus dem Backtest-Request (nicht optimierbar).
---
## J — RUN HASH / REPRODUCIBILITY
Phase-2-Parameter, die die Run-Identität beeinflussen müssen (Kandidaten):
- `execution_model_version`
- `spread_model_version`
- `slippage_model_version`
- `cost_model_version`
- `intrabar_policy_version`
**Entscheidung** (Grundregel: Daten ≠ Ausführungsmodell ≠ Strategieparameter):
- **Dataset-Hash** (data_hash): rein die OHLC/Bar-Daten (bid/ask). Unverändert durch Execution-Modelle.
- **Run-Hash**: Dataset-Hash + Strategie+Param + **Execution/Broker/Cost-Modell-Versionen** (weil anderer Execution-Modus → anderes Ergebnis → anderes run_hash, sonst falscher Cache-Hit).
- **Audit**: alle expliziten Modelle/Versions + breakdown, aber NICHT in den Hash (nur repräsentativ).
- M13-Optimizer-Hash analog: Execution-Modell-Versionen zusätzlich.
---
## K — RESULT METRICS
Heute (`_compute_metrics`): total_trades, winrate, profit_factor, expectancy_r, avg_r, net_pnl, gross_profit, gross_loss, avg_holding_h, long_count, short_count, max_drawdown, by_regime. **Kein Kosten-Breakdown.**
**Zielmetriken (Phase-2-Design)**:
- `gross_pnl` (ohne Kosten)
- `net_pnl` (nach allen Kosten)
- `spread_cost` / `slippage_cost` / `commission_cost` / `financing_cost` / `total_cost`
- `ambiguous_bars` (Bars mit Intrabar-Ambiguität)
- `gap_fills`
- `conditional_bars_used` (V2-Conditional)
- `reference_feed_warning`
---
## L — FAIL-CLOSED CONDITIONS (Phase-2)
Design-Liste, Situationen in denen Phase-2-M12 NICHT rechnen darf:
| Fall | Error-Code |
|---|---|
| Bid/Ask erforderlich aber fehlt | `BID_ASK_REQUIRED_MISSING` |
| unbekannte price_basis | `UNKNOWN_PRICE_BASIS` |
| unbekanntes execution_model | `UNKNOWN_EXECUTION_MODEL` |
| Fixture blockiert | `FIXTURE_DATA_BLOCKED` |
| Dataset nicht eligible | `DATASET_NOT_ELIGIBLE` / `UNKNOWN_CRITICAL_METADATA` |
| DatasetContext fehlt | `DATASET_CONTEXT_MISSING` |
| Kostenmodell verlangt Brokerdaten fehlen | `COST_MODEL_BROKER_DATA_MISSING` |
| BROKER_REPLAY + REFERENCE-Feed | `BROKER_REPLAY_REFERENCE_INCOMPATIBLE` |
| Tick-Modus verlangt Tickdaten, hat nur M1 | `TICK_DATA_REQUIRED_MISSING` |
---
## M — MIGRATION / COMPATIBILITY
| Modus | Daten | Execution | Spread | Slippage | Costs | Intrabar | Audit | Reproducibility | Zulässig |
|---|---|---|---|---|---|---|---|---|---|
| **LEGACY Single-Price** | ohlcv single | open/close, Stop-first | Config-punkte | Config-punkte | Config-fix/% | Stop-first | Legacy-Hash | run_hash | ✅ |
| **V2 REFERENCE BID/ASK** | historical bid/ask | bid/ask-Fill | intrinsic (bid/ask) | Config/Determ. | Config-fix/% | wählbar | full-Audit | run_hash + execution_version | ✅ (nach Implementierung) |
| **BROKER BID/ASK** | Broker bid/ask | broker-Fill | real broker | real broker | real broker | broker | full | Broker-Feed-Audit | (künftig) |
| **TICK** | tick-data | tick-seq | real | real | real | tick-resolved | full | Tick-Audit | (künftig) |
Legacy bleibt **unverändert** (durch `price_basis`/`execution_model`-Gates). Kein stiller Fallback.
---
## N — IMPLEMENTIERUNGSREIHENFOLGE (begründet)
Code-Audit belegt, dass die Engine rein Single-Price ist. Empfohlene Reihenfolge (jede Phase testbar + rückbaubar):
1. **ExecutionContext** (Datenmodell/Versionierung) — Voraussetzung, trennt alles. LOW
2. **Bid/Ask-Datenmodell** (load_candles/bars trägt bid/ask; Engine liest nach price_basis) — MEDIUM
3. **Bid/Ask Fill** (LONG buy ask/sell bid, SHORT bid/ask) — HIGH (Fill-Preis)
4. **Spread-Modell** (bid_ask intrinsic vs synthetic, keine Doppelzählung) — HIGH
5. **Slippage** (slippage_model, Entry/Exit-Einheit, Determinismus) — MEDIUM
6. **Cost-Breakdown** (Commission/Financing getrennt, nicht nur pauschal) — MEDIUM
7. **Gap-Fills** (konsistentes Gap-Open-Verhalten, Stop+Target) — HIGH
8. **Intrabar-Policy** (PESSIMISTIC/OPTIMISTIC/STOP_FIRST/TARGET_FIRST/TICK/UNKNOWN-BLOCK) — MEDIUM
9. **Result-Cost-Breakdown** (gross/net/cost-Metriken) — LOW
10. **M13-Param-Grenzen** (nur STRATEGY_PARAMETER optimizierbar, Execution-Kosten fix) — MEDIUM
11. **Reproducibility** (execution_model_version in run_hash, Audit-Vollständigkeit) — MEDIUM
12. **Regression/Legacy** (unverändert; Gate-Zusammenheit) — LOW
13. **Acceptance-Tests** (Plan unten)
Die Reihenfolge weicht von der (User)-Suggestions ab: **ExecutionContext-Versionierung zuerst** ist zwingend, sonst werden Modelle ohne Versionierung eingebaut und Reproducibility bricht nachträglich. Bid/Ask-Datenmodell vor Fill zwingend.
---
## RISIKO-MATRIX
| Änderung | Modul | Datei/Fn | Risiko | Regression | Test | Rollback |
|---|---|---|---|---|---|---|
| ExecutionContext | M12 | service.py/engine.py | LOW | — | Hash-Vergleich | revert service+engine |
| Bid/Ask-Datenmodell | M12 | marketdata/client.py, repo | **MEDIUM** | Daten-Format | load-candles-Pfad | revert client/repo |
| Bid/Ask-Fill | M12 | engine.py | **HIGH** | Fill-Preis | LONG/SHORT Fill-Test | revert engine |
| Spread-Modell | M12 | engine.py | **HIGH** | Spread, PnL | No-Doppelzählung | revert |
| Slippage | M12 | engine.py | MEDIUM | slippage-Einheit | Entry/Exit-Slipp | revert |
| Commission/Costs | M12 | engine.py | MEDIUM | Cost-Breakdown | Fee-Test | revert |
| Gap-Fills | M12 | engine.py | **HIGH** | Stop/Target, PnL | Gap-Tests | revert |
| Intrabar-Policy | M12 | engine.py | MEDIUM | Stop/Target | Intrabar-Tests | revert |
| Result-Cost-Breakdown | M12 | engine.py | **HIGH** | PnL | Metrics | revert |
| M13-Param-Grenzen | M13 | optimizer.py | MEDIUM | Overfit | Optimizer-Test | revert |
| Reproducibility | M12/M13 | service.py/optimizer.py | MEDIUM | Run-Hash | Hash-Repro | revert |
---
## ACCEPTANCE PLAN (vor Implementierung — Katalog)
**Fill/Spread:**
- LONG Entry Ask, LONG Exit Bid (BID-ASK)
- SHORT Entry Bid, SHORT Exit Ask
- echte bid/ask-Spread NICHT doppelt (Bid/Ask-Modus)
- synthetischer Spread korrekt (Single-Price-Modus)
**Slippage/Cost:**
- Slippage Long/Short adversarial
- Commission korrekt (fix+%)
**Intrabar/Gap:**
- Stop-only, Target-only, Stop+Target gleiche Bar
- Gap über Stop, Gap über Target
- PESSIMISTIC/OPTIMISTIC unterscheidbar
**Feed/Klassifizierung:**
- REFERENCE korrekt markiert
- BROKER_REPLAY + REFERENCE → fail-closed
**Reproduzierbarkeit:**
- gleicher Input → identisches Ergebnis
- andere Execution-Version → anderer run_hash
**Regression:**
- Legacy unverändert
---
## OFFENE ENTSCHEIDUNGEN (für Christian)
1. **Spread-Anwendung**: heutiger doppelter Entry+Exit-Spread → auf **einmaliger Roundtrip** oder je Seite ändern? Empfehlung: je Seite (realistisch).
2. **Intrabar-Default**: PESSIMISTIC (heutig, konservativ) als Standard bis Tick? Oder OPTIMISTIC?
3. **Slippage-Form**: deterministisch (Punkte) oder stochastisch (Seed)? Empfehlung: deterministisch default, stochastisch nur mit Seed+Hash.
4. **Cost-Modell-Version**: initial simple (fix+%, Spread/Punkte) → spätere Finanzierung/Swap/GS-Premium nachgeschaltet?
5. **Bid/Ask-Datenmodell**: sollen V2-bid/ask OHLC-Daten direkt in Engine (execution_model=APPROXIMATION) oder erst als REFERENCE-Feed (MARKET_SIMULATION) weitergegeben werden? Empfehlung: bid/ask zuerst nur in `load_bars_with_quality`, Engine-Pfad weiterhin single, bis Fill implementiert.
6. **M13**: Execution-Costs fix → ja (nicht optimizierbar) bestätigt.
7. **feed_type-Klassifizierung**: MARKT_SIMULATION vs BROKER_APPROXIMATION Bezeichnungen beibehalten?
---
## EXAKTER NÄCHSTER IMPLEMENTIERUNGSSCHRITT
Sobald Christian die offenen Entscheidungen freigibt, ist der **1. Schritt von Phase 10**:
1. **ExecutionContext-Dataclass** (execution_model, spread_model, slippage_model, cost_model, intrabar_policy, price_basis, feed_type + je *_version) — **ausser** DatasetContext, reine Ausführungs-Konfiguration.
2. Engine lädt als Einzelz: `execution_model` in `BacktestRequest`.
3. Tests: deterministisch, Hash-Integration, Legacy unverändert.
**NOCH NICHT implementiert** (verboten bis Freigabe): Bid/Ask-Fill, Spread-Änderung, Slippage-Änderung, Kostenmodell-Änderung, Intrabar-Ausführungslogik, Jahresbackfill, IG-Calls, Orders, Docker-Netzwerkänderung.