trading-system-docs/notes/trading/system-docs/phase9_readiness_audit.md

341 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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.