# MS-MTF-D-SHADOW-LOGGER-1 — Plan-Only **Datum:** 2026-06-05 **Modus:** PLAN-ONLY · 0× Code · 0× Push · 0× Orders · 0× Runtime-Apply · 0× Param-Change **Vorgängerplan:** MS-MTF-D-PROTECTED-SHADOW-PLAN (2026-06-04 22:39 UTC) — durch diese Adjust-Version ersetzt **Walk-Forward-validierte Basis-Konfig (unverändert, NICHT Scope dieser Phase):** BE 0.8 %/0.8 %, Partial 1.5 %/30 %, Trail 2.0 %/0.5 %, MTF-D 3-of-4, 4h Hold --- ## 1. Ziel Erweitere das existierende MS-MTF-1 (4h-Confirmation) **read-only** auf 8h+12h Confirmation und logge die neue 3-of-4-MTF-D-Entscheidung neben der bestehenden Live-Entscheidung. Sammle 2–4 Wochen Vergleichsdaten für eine spätere Pilot-Entscheidung. **Diese Phase ändert keine Protected-/Exit-Parameter und aktiviert keine MS-Live-Logik.** Sie erweitert nur das Shadow-Logging um 8h/12h MTF-D. **Boundaries:** Bot bleibt im `MULTI_STRATEGY_DRY_RUN=true`. Kein Position-Manager-Parameter-Touch. Keine Execution-Logik-Änderung. Keine SL/TP/Trailing/Partial-Werte angefasst. --- ## 2. Cross-Impact-Matrix (Pflicht per CLAUDE.md §2) | Changed Area | Direct Effect | Downstream Consumers | Required Follow-up | |---|---|---|---| | `base_strategy.py` | neue Methode `_compute_mtf_d_shadow()` | wird vom MS-Runner (nicht von Strategy-Subklassen) gerufen — Subklassen NICHT geändert | Tests in base_strategy + MS-Runner | | `data_fetcher.py` | 2 neue cached fetcher: `fetch_ohlcv_8h_cached`, `fetch_ohlcv_12h_cached` | nur MS-Runner | siehe §6 Cache-Lifecycle | | `multi_strategy_runner.py` | holt 8h+12h vor strategy-loop, berechnet shadow, passt durch | MS-Runner-Tests | – | | `multi_strategy_runner.py` | erweitert `_log_decision` + `emit_decision()` metadata mit `mtf_shadow` | `decision_logs.metadata_json`, `multi_strategy.log` JSONL | metadata_json bekommt neue Keys, KEIN DB-Migration nötig | | `settings.py` | **NEU:** `MS_MTF_D_SHADOW_ENABLED` Kill-Switch (default true) | MS-Runner | siehe §3 | | `position_manager.py` | **0** | – | – | | `live_trade.py`, `trader_core.py` | **0** | – | – | | GUI, CommandBus, DB-Schema, env-files | **0 außer optional MS_MTF_D_SHADOW_ENABLED** | – | – | **Mainnet-Status:** unverändert verboten. **MS-Live-Status:** unverändert DRY_RUN. **Live-Trade-Entscheidungen:** unverändert (Shadow ist NIE blockierend). --- ## 3. Kill-Switch (Soft-Disable) ### Neuer Setting ```python # trading/config/settings.py MS_MTF_D_SHADOW_ENABLED: bool = ( os.getenv('MS_MTF_D_SHADOW_ENABLED', 'true').lower() == 'true' ) ``` ### Verhalten - **Default Code/Config:** `true` (aktiv) - **Env kann optional auf `false` gesetzt werden** — falls im Betrieb unerwartete Tracebacks oder API-Probleme entstehen - **Kein Env-Change beim Cutover nötig.** Der Shadow-Logger ist standardmäßig aktiv. Der Kill-Switch existiert als Operations-Sicherheitsnetz, nicht als Aktivierungsschritt. ### MS-Runner-Pseudocode ```python mtf_shadow = None if self.settings.MS_MTF_D_SHADOW_ENABLED: try: mtf_shadow = self._compute_mtf_d_shadow(df, df_4h, df_8h, df_12h) except Exception as e: logger.debug(f"[MS-MTF-D-SHADOW] compute failed for {symbol}: {e}") mtf_shadow = None # mtf_shadow wird in metadata gepasst (None wenn disabled oder Fehler) ``` ### Hard-Rollback bei Live-Problem 1. Operator setzt `MS_MTF_D_SHADOW_ENABLED=false` in `.env` 2. `docker compose up -d clawbot` (recreate) 3. Investigation 4. Falls Code-Fix nötig: separate P0-Phase --- ## 4. Geänderte Dateien (Plan-Spec) ### 4.1 `trading/scanner/data_fetcher.py` — 2 neue Cached Fetcher Identische Struktur wie existierendes `fetch_ohlcv_4h_cached`: ```python def fetch_ohlcv_8h_cached(self, symbol, exchange_id='binance') -> Optional[pd.DataFrame]: """MS-MTF-D-SHADOW-LOGGER-1 (2026-06-05) — 8h Cache, TTL 15 min.""" ... def fetch_ohlcv_12h_cached(self, symbol, exchange_id='binance') -> Optional[pd.DataFrame]: """MS-MTF-D-SHADOW-LOGGER-1 (2026-06-05) — 12h Cache, TTL 15 min.""" ... ``` Plus in `__init__`: ```python self._ohlcv_8h_cache: dict = {} self._ohlcv_12h_cache: dict = {} self._ohlcv_8h_cache_ttl_s: int = 15 * 60 self._ohlcv_12h_cache_ttl_s: int = 15 * 60 ``` **LoC:** ~50. ### 4.2 `trading/strategies/base_strategy.py` — Shadow-Helper ```python # ── MS-MTF-D-SHADOW-LOGGER-1 (2026-06-05): 3-of-4 MTF Shadow Audit ───── def _compute_mtf_d_shadow(self, df_1h: Optional[pd.DataFrame], df_4h: Optional[pd.DataFrame], df_8h: Optional[pd.DataFrame], df_12h: Optional[pd.DataFrame]) -> Dict[str, Any]: """Read-only MTF-D Shadow. Bullish-Score je TF: +35 wenn close > EMA20 +35 wenn EMA20 > EMA50 +30 wenn close > EMA50 bullish = score >= 50 Returns Dict mit: score_1h, score_4h, score_8h, score_12h (int 0..100 oder None bei fehlenden Daten) bullish_1h, bullish_4h, bullish_8h, bullish_12h (bool oder None) available_count (int 0..4) missing_timeframes (list[str]) bullish_count (int 0..4) — nur Bullish-Treffer auf verfügbaren TF shadow_status: 'complete' | 'incomplete' incomplete_reason (str | None) mtf_d_pass (bool | None) — None wenn shadow_status='incomplete' shadow_reject_reason (str | None) — nur gesetzt bei shadow_status='complete' und mtf_d_pass=False NIEMALS verwendet zur Blockade. Nur log/audit. """ def _score_one(df, label): if df is None or len(df) < 50: return (None, None, label) try: ema20 = df['close'].ewm(span=20, adjust=False).mean().iloc[-1] ema50 = df['close'].ewm(span=50, adjust=False).mean().iloc[-1] close = float(df['close'].iloc[-1]) score = 0 if close > ema20: score += 35 if ema20 > ema50: score += 35 if close > ema50: score += 30 return (score, score >= 50, None) except Exception: return (None, None, label) s1, b1, m1 = _score_one(df_1h, '1h') s4, b4, m4 = _score_one(df_4h, '4h') s8, b8, m8 = _score_one(df_8h, '8h') s12, b12, m12 = _score_one(df_12h, '12h') missing = [m for m in (m1, m4, m8, m12) if m is not None] available = 4 - len(missing) bullish_count = sum(1 for b in (b1, b4, b8, b12) if b is True) # Strikte Interpretation: incomplete bei JEDEM fehlenden TF. # Fehlende TFs werden NICHT als bearish gezaehlt. shadow_status = 'complete' if available == 4 else 'incomplete' incomplete_reason = None mtf_d_pass = None reject = None if shadow_status == 'complete': mtf_d_pass = bullish_count >= 3 if not mtf_d_pass: reject = f"mtf_d_only_{bullish_count}_of_4_bullish" else: incomplete_reason = "missing_" + "+".join(missing) + "_ohlcv" # mtf_d_pass bleibt None — incomplete darf NICHT als echter Reject gewertet werden. return { 'score_1h': s1, 'bullish_1h': b1, 'score_4h': s4, 'bullish_4h': b4, 'score_8h': s8, 'bullish_8h': b8, 'score_12h': s12, 'bullish_12h': b12, 'available_count': available, 'missing_timeframes': missing, 'bullish_count': bullish_count, 'shadow_status': shadow_status, 'incomplete_reason': incomplete_reason, 'mtf_d_pass': mtf_d_pass, 'shadow_reject_reason': reject, } ``` **LoC:** ~70. **Wichtig:** Die existierende `_is_4h_confirming` Methode bleibt **unverändert** und wird weiterhin als Live-Gate genutzt. Die neue `_compute_mtf_d_shadow` ist rein Audit/Shadow. ### 4.3 `trading/strategies/multi_strategy_runner.py` — Fetch + Forward In `_evaluate_symbol`, nach dem existierenden `df_4h`-Fetch: ```python # MS-MTF-D-SHADOW-LOGGER-1 (2026-06-05): zusaetzlich 8h + 12h fuer Audit. df_8h = None df_12h = None if self.settings.MS_MTF_D_SHADOW_ENABLED: try: df_8h = self._data_fetcher.fetch_ohlcv_8h_cached(symbol) df_12h = self._data_fetcher.fetch_ohlcv_12h_cached(symbol) except Exception as e: logger.debug(f"[MS-MTF-D-SHADOW] {symbol}: 8h/12h fetch failed ({e})") ``` In der per-strategy loop, nach `strategy_result = strategy.score_signal(...)`: ```python # Shadow: berechne MTF-D Audit (nur Logging, KEIN Blocking) mtf_shadow = None if self.settings.MS_MTF_D_SHADOW_ENABLED: try: mtf_shadow = strategy._compute_mtf_d_shadow(df, df_4h, df_8h, df_12h) except Exception as e: logger.debug(f"[MS-MTF-D-SHADOW] compute failed: {e}") ``` In `_log_decision`, neuer Eintrag im JSONL record: ```python 'mtf_shadow': mtf_shadow, ``` In `emit_decision` metadata: ```python metadata = { ...existing keys..., 'mtf_shadow': mtf_shadow, } ``` **LoC:** ~30. ### 4.4 Tests — `trading/tests/test_ms_mtf_d_shadow.py` (NEU) Mindestens 18 Tests: 1. Empty df → score None, status='incomplete', incomplete_reason='missing_1h_ohlcv' 2. Bullish strong (alle 4 TF) → status='complete', mtf_d_pass=True, bullish_count=4 3. 3-of-4 bullish (1 bearish, alle complete) → pass=True, count=3, status=complete 4. 2-of-4 bullish (alle complete) → pass=False, count=2, reject set, status=complete 5. 0-of-4 bullish → pass=False, count=0, reject set, status=complete 6. Mixed scores threshold edge (50/49/100/100) → 3 bullish (49 not enough), pass=True 7. df_8h fetcher returns None defensive → status='incomplete', missing=['8h'], pass=None 8. df_12h fetcher returns None defensive → status='incomplete', missing=['12h'], pass=None 9. df_8h AND df_12h beide None → status='incomplete', missing=['8h','12h'], available_count=2, pass=None 10. Cache TTL respected (smoke + reuse) 11. Negative-cache TTL (1/3 of normal) respected 12. shadow NEVER blocks: even with mtf_d_pass=False, decision goes through 13. JSONL record contains mtf_shadow key (or None when disabled) 14. emit_decision metadata contains mtf_shadow 15. Backward compat: existing _is_4h_confirming unchanged 16. Existing trend_follow tests pass (no regression) 17. Existing breakout tests pass (no regression) 18. Kill-Switch off (`MS_MTF_D_SHADOW_ENABLED=false`): mtf_shadow = None im record Plus Regression-Sweep: - 20 SyncSanity - 20 ExitReasonFix - 20 MS-Dedup - 12 Stablecoin - 32 RECON-1 - 15 MS-MTF-1 (existing 4h gate) --- ## 5. Was NICHT geändert wird | Item | Aktueller Wert | Bleibt | |---|---|---| | `MULTI_STRATEGY_DRY_RUN` | true | true | | `BREAKEVEN_GAIN_TRIGGER` (position_manager.py:37) | 0.008 | 0.008 | | `BREAKEVEN_BUFFER_PCT` (position_manager.py:32) | 0.008 | 0.008 | | `PARTIAL_PROFIT_T1_TRIGGER` | 0.015 | 0.015 | | `PARTIAL_PROFIT_T1_SELL_PCT` | 0.30 | 0.30 | | Trailing Trigger (apply_trailing) | implizit 2.0 % | implizit 2.0 % | | Trailing Floor | 0.5 % | 0.5 % | | `MS_CANDIDATE_COOLDOWN_*` | aktiv | aktiv | | Stablecoin-Block | aktiv | aktiv | | RECON-1 | aktiv | aktiv | | 4h-Time-Stop | nicht vorhanden | bleibt nicht vorhanden | | MS-MTF-1 (4h-Live-Gate) | aktiv | aktiv (parallel zu Shadow) | | `_is_4h_confirming` API | unverändert | unverändert | | Strategy-Subklassen | unverändert | unverändert | | Live-Trade-Entscheidungen | unverändert | unverändert | --- ## 6. Cache-Lifecycle ### Aktueller Stand (wichtig zu wissen) - `DataFetcher` wird **pro `MultiStrategyRunner`-Instanz** angelegt - `MultiStrategyRunner` wird **pro Scan-Cycle** instanziiert (main.py:1447) - Folge: das In-Memory-Cache (`_ohlcv_4h_cache`, ab MS-MTF-D-SHADOW: `_ohlcv_8h_cache`, `_ohlcv_12h_cache`) lebt nur **innerhalb eines Scan-Cycles** ### Konsequenz für Shadow-Phase - 15-Minuten-TTL gilt theoretisch, aber wird in der Praxis pro Scan-Cycle resetted - Pro Scan-Cycle: 1× pro Symbol pro Timeframe Binance-Fetch (3 zusätzliche Calls je Symbol) - Pro Scan-Cycle: ~25 Symbols × 3 TF = ~75 zusätzliche Binance-Calls (3 × heutige 4h-Calls) - Bei Scan alle ~2.5 min: ~30 Scans/h × 75 = **~2 250 zusätzliche Calls/h** = ~38/min ### Bewertung gegen Binance-Limit Binance Public-API erlaubt 1 200 req/min anonym. 38/min ist deutlich unter Limit. **Akzeptabel ohne persistenten Cache.** ### Optionale spätere Phase Für echten scan-übergreifenden Cache mit echter 15-Min-TTL wäre persistenter File-/Redis-/Shared-Cache nötig. Als Follow-up dokumentiert (**MS-MTF-CACHE-PERSIST-1**, P3), **nicht** Scope dieser Phase. ### Cutover-Check **API-Last-Monitoring als Cutover-Pflichtprüfung ergänzt:** §8 Step 10 erweitert um Binance-Rate-Limit-Beobachtung in den ersten 4 h. --- ## 7. Shadow-Logging-Schema (mit Missing-Data-Status) Jedes Decision-Log (`multi_strategy.log` JSONL + `decision_logs.metadata_json`) bekommt zusätzlich: ### Beispiel: complete + pass ```json { "ts": "2026-06-05T00:42:00Z", "symbol": "TON/USDT", "strategy_id": "trend_follow", "decision": "TRADE_CANDIDATE", "mtf_shadow": { "score_1h": 100, "bullish_1h": true, "score_4h": 100, "bullish_4h": true, "score_8h": 65, "bullish_8h": true, "score_12h": 35, "bullish_12h": false, "available_count": 4, "missing_timeframes": [], "bullish_count": 3, "shadow_status": "complete", "incomplete_reason": null, "mtf_d_pass": true, "shadow_reject_reason": null } } ``` ### Beispiel: complete + reject ```json { "mtf_shadow": { "score_1h": 100, "bullish_1h": true, "score_4h": 65, "bullish_4h": true, "score_8h": 30, "bullish_8h": false, "score_12h": 35, "bullish_12h": false, "available_count": 4, "missing_timeframes": [], "bullish_count": 2, "shadow_status": "complete", "incomplete_reason": null, "mtf_d_pass": false, "shadow_reject_reason": "mtf_d_only_2_of_4_bullish" } } ``` ### Beispiel: incomplete (Daten fehlen — NICHT als Reject zählen) ```json { "mtf_shadow": { "score_1h": 100, "bullish_1h": true, "score_4h": 100, "bullish_4h": true, "score_8h": 65, "bullish_8h": true, "score_12h": null, "bullish_12h": null, "available_count": 3, "missing_timeframes": ["12h"], "bullish_count": 3, "shadow_status": "incomplete", "incomplete_reason": "missing_12h_ohlcv", "mtf_d_pass": null, "shadow_reject_reason": null } } ``` ### Beispiel: Shadow disabled (Kill-Switch off) ```json { "mtf_shadow": null } ``` ### Strikte Regeln für die Analyse - `mtf_d_pass = true` → klare Bestätigung; nur zählen wenn `shadow_status="complete"` - `mtf_d_pass = false` → echter Reject; nur zählen wenn `shadow_status="complete"` - `mtf_d_pass = null` → **nicht** als Reject werten; Datenlücke dokumentieren - `mtf_shadow = null` → Shadow disabled oder Compute-Fehler; separat aggregieren --- ## 8. Cutover-Plan (SOT-1d, falls Operator GO gibt) | Step | Action | |---|---| | 1 | Crontab `bot_watchdog.sh` freeze via `# CUTOVER_FREEZE_MTF_D_SHADOW` | | 2 | Backup `/root/mtf-d-shadow-backup-{TS}/` (3 Code-Files + 1 Test + 1 Settings-Diff + Tail `multi_strategy.log`) | | 3 | `docker compose build clawbot` | | 4 | `docker compose up -d --force-recreate clawbot` (worker NICHT betroffen — keine Schema-/Position-Manager-Änderung) | | 5 | 3-Way MD5: `base_strategy.py` + `data_fetcher.py` + `multi_strategy_runner.py` + `settings.py` (Repo == Image == Container) | | 6 | Bot health-check post-recreate (0 Tracebacks) | | 7 | Crontab thaw | | 8 | Live-Verify: erste 2 Scan-Cycles, prüfe `multi_strategy.log` enthält `mtf_shadow` Keys mit `shadow_status` | | 9 | DB-Verify: `decision_logs.metadata_json.mtf_shadow` für post-cutover-Rows | | 10 | **API-Last-Check (NEU):** Binance-Call-Volume in den ersten 4 h überwachen; erwartete ~38/min, hart bei > 200/min reagieren | | 11 | 24h-Monitor: keine neuen Tracebacks, MS DRY-RUN-Quote unverändert | --- ## 9. Monitoring-Checkliste ### Phase A — Cutover Smoke (24 h) | Check | Wo | Erwartung | |---|---|---| | 0 neue Tracebacks | `bot_stdout.log` | ✓ | | `mtf_shadow` Schlüssel in jeder neuen Decision | `multi_strategy.log` | ✓ | | `shadow_status` ∈ {complete, incomplete} | log + DB | ✓ | | 4h+8h+12h Cache populated (per cycle) | DataFetcher state | ✓ | | Binance API-Last unter 200/min | bot stdout / network | ✓ | | Bot host-PID stable | `bot.pid` | unverändert | | MS Stablecoin-Block + RECON-1 weiterhin live | `bot_statuses.metadata_json` | ✓ | | Bestehende MS-MTF-1 (`_is_4h_confirming`) entscheidet weiterhin | Log | ✓ | | Kill-Switch funktional (probehalber `=false` setzen → `mtf_shadow=null`) | optional Smoke | ✓ | ### Phase B — Shadow-Period (≥ 2 Wochen, besser 4 Wochen) | Metric | SQL/jq | |---|---| | Anzahl TRADE_CANDIDATEs total | `count(*) where decision='TRADE_CANDIDATE'` | | Davon shadow_status='complete' | `count(*) where mtf_shadow.shadow_status='complete'` | | Davon shadow_status='incomplete' | `count(*) where mtf_shadow.shadow_status='incomplete'` | | Bei complete: mtf_d_pass=true | `count(*) where mtf_shadow.mtf_d_pass=true` | | Bei complete: mtf_d_pass=false | `count(*) where mtf_shadow.mtf_d_pass=false` | | Discrepanz Live-MTF-1 vs MTF-D | per-row diff (nur complete-Rows) | | Per-Strategy Shadow-Filter-Rate | groupby strategy_id | | Bullish-Count-Verteilung (0/1/2/3/4) | histogram (nur complete) | | Reject-Reason-Cluster | distinct mtf_shadow.shadow_reject_reason | | Missing-Timeframe-Häufung | groupby missing_timeframes (incomplete-Rows) | ### Phase C — Post-Shadow Re-Eval (nach 2-4 Wochen) - 180-d-Replay Re-Run mit neuen Daten - Walk-Forward-Validation Re-Run - Realistic-fill Backtest auf neuer Period - MS-MTF-D Empirical-Effect: hätte 3-of-4 Filter wie viele wirklich profitable Candidates gefiltert? - Bootstrap auf complete-only-Subset - Akzeptanz-Check pro Operator-Schwellen --- ## 10. Akzeptanz-Schwellen für Pilot-Entscheidung (post-Shadow) | Schwelle | Wert | Quelle | |---|---|---| | Test PF ≥ 1.5 | 1.5 | Operator-Vorgabe | | Expectancy > 0 | > 0 | Operator-Vorgabe | | Test P(loss) niedrig | ≤ 5 % | Operator-Vorgabe | | Max Loss-Streak ≤ 7 | 7 | konservativer Floor | | Worst-Day-PnL ≥ −3 % | −3 % | konservativer Tagesfloor | | 0 STATE-COHERENCE-Warnings | 0 | RECON-1-Health | | Stablecoin-Block-Trefferquote ≥ aktuell | aktuell (~64 RLUSD/Stunde) | Filter funktional | | Dedup-Filter aktiv und > 0 | > 0 (sobald Repeats) | RECON-1 | | `shadow_status='complete'`-Rate ≥ 90 % | 90 % | Daten-Qualität | | Coin-Allowlist | KEINE | Operator-Boundary | | Coin-Denylist | KEINE | Operator-Boundary | **Wenn alle Schwellen erfüllt:** Phase „MS-LIVE-PROTECTED-PILOT-1" planen. **Wenn ≥ 1 Schwelle scheitert:** weitere Shadow-Period oder Hyperparameter-Anpassung. --- ## 11. Risiken | # | Risiko | Severity | Mitigation | |---|---|---|---| | 1 | 8h+12h-Fetch erhöht API-Last (pro Cycle 3× Calls/Symbol) | Niedrig | ~38 Calls/min ≪ Binance 1200/min; API-Monitoring im Cutover (§8 Step 10) | | 2 | DataFetcher Cache-Eviction → Memory-Leak | Niedrig | cycle-lokales Cache → kein Leak (siehe §6) | | 3 | Shadow-Logic verändert Live-Verhalten versehentlich | Hoch (wenn fehlerhaft) | Strikte Read-Only Garantie: `_compute_mtf_d_shadow` returnt Dict, wird NIE auf decision/reject_reason gemapped. Test 12 prüft dies explizit. Kill-Switch §3 als Backup. | | 4 | metadata_json wächst — DB-Performance | Niedrig | mtf_shadow ist ~300 Bytes/Row, JSONB-effizient | | 5 | Missing-Data-Status wird falsch als Reject interpretiert | **Hoch** wenn ignoriert | Strikte Trennung: `mtf_d_pass=null` bei incomplete; Test 7-9 prüfen das; Auswertung MUSS complete-only filtern | | 6 | 4-Wochen Shadow ist lang | Mittel | Operator kann nach 2 Wochen Zwischen-Check (kein finales Akzeptanz-Verdict) | | 7 | Marktregime ändert sich → Shadow-Daten unrepräsentativ | Mittel | dokumentieren; Re-Eval mit aktualisiertem Markt | | 8 | Persistenter Cache fehlt → 15-Min-TTL effektiv nicht greifend | Niedrig | dokumentiert in §6; Follow-up MS-MTF-CACHE-PERSIST-1 (P3) | --- ## 12. Rollback-Plan ### Soft-Rollback (Code-Bug, keine State-Corruption) 1. `git revert ` der MS-MTF-D-SHADOW-LOGGER-Änderung 2. SOT-1d: `docker compose build clawbot && docker compose up -d --force-recreate clawbot` 3. 3-Way MD5 check 4. Healthcheck ### Hard-Rollback (falls Shadow-Logik versehentlich blockiert) 1. Sofort: env `MS_MTF_D_SHADOW_ENABLED=false` setzen 2. Bot recreate 3. Investigation 4. Code-Fix als P0 ### Daten-Rollback - `multi_strategy.log` Append-Only: kein State-Issue - `decision_logs.metadata_json` nur neuer Key → keine Migration nötig **Backup-Pfad:** `/root/mtf-d-shadow-backup-{TS}/` mit 4 Code-Files + Tail von `multi_strategy.log` + `live_portfolio.json`. --- ## 13. Operator-Entscheidungen vor `GO EXECUTE` | Q | Frage | Default-Empfehlung | |---|---|---| | Q1 | Soft-Kill-Switch (`MS_MTF_D_SHADOW_ENABLED` env) einbauen? | **A** Ja — default true, env-disable nur als Notbremse | | Q2 | Cache-TTL für 8h+12h? | **A** 15 min (identisch zu 4h) — effektive Wirkung cycle-lokal (siehe §6) | | Q3 | Negative-cache TTL für failed fetches? | **A** 1/3 von 15 min (5 min), wie bei 4h | | Q4 | `mtf_shadow` in beide Quellen (JSONL + decision_logs)? | **A** ja, beide für Redundanz | | Q5 | Bullish-Score-Threshold 50? | **A** ja (50 aus Backtest-Methodologie validiert) | | Q6 | 0.7/0.7 als separater Shadow-Comparator? | **B** nein (post-Shadow-Phase separat) | | Q7 | Shadow-Period-Dauer? | **B** 4 Wochen (sicherer als 2) | | Q8 | Cutover sofort nach GO EXECUTE oder am Wochenende? | Wochenende (geringere Last) | | Q9 | Missing-Data-Status interpretieren als | **A** `mtf_d_pass=null`; NICHT als Reject zählen | --- ## 14. Geschätzter Aufwand | Item | LoC | Tests | Aufwand | |---|---:|---:|---| | `data_fetcher.py` 8h+12h Cached | 50 | 4 | 30 min | | `base_strategy.py` `_compute_mtf_d_shadow` (mit Missing-Status) | 70 | 9 | 60 min | | `multi_strategy_runner.py` integration | 30 | 3 | 30 min | | `settings.py` Kill-Switch | 5 | 1 | 10 min | | Tests neu | – | 18 | 75 min | | Tests Regression-Sweep | 0 | – | 30 min | | Code-Review + Cutover-Prep | – | – | 30 min | | **TOTAL** | **~155** | **35 neu** | **~4 h** | Roadmap-Commit + Push als separate Phase. --- ## 15. Verhältnis zu Strategy-Loss-Backtest-Audit **Strategy-Loss-Backtest-Audit bleibt wichtig**, blockiert diese Shadow-Logging-Phase aber **nicht**, da diese Phase: - read-only ist (keine Trades, kein Risk-Parameter, keine Exit-Logik) - die existierende Live-Pipeline (Legacy + bestehende MS-MTF-1) unverändert lässt - ein optionaler Kill-Switch im Notfall greift - die Shadow-Daten **unabhängig** vom Strategy-Loss-Audit ausgewertet werden können → Strategy-Loss-Backtest-Audit ist als **paralleler P1-Track** weiterhin offen, aber **kein Pre-Requisite** für Shadow-Logger-Cutover. --- ## 16. Boundaries 0× Bot-Code-Touch (außer für diese Plan-Phase als Code-Phase) · 0× Trading-State · 0× Orders · 0× MS-Live-Aktivierung · 0× Mainnet · 0× Env-Änderung (außer optional Kill-Switch in `.env`) · 0× DB-Schema · 0× DB-Migration · 0× ConfigProfile-Apply · 0× Bot-Recreate (Plan-Phase) · 0× Worker-Recreate (Plan-Phase) · 0× Push (Plan-Phase) · 0× Roadmap-Commit (Plan-Phase) · 0× Coin-Allowlist · 0× Coin-Denylist · 0× Param-Change (BE/Partial/Trailing/Hold). --- ## 17. STOP-Bedingungen Plan **NICHT umsetzen** wenn: - Operator-GO fehlt - Bot oder Worker unhealthy - Aktiver State-Coherence-Conflict im laufenden Bot - MS-Dry-Run-Anomalien in den letzten 24h - Binance Public-API erreichbar unter Limit (nicht gerade Rate-Limited) **Nicht mehr als STOP-Bedingung:** Strategy-Loss-Backtest-Audit-Status (siehe §15). --- ## 18. Plan-Closure (per CLAUDE.md §13) | Pflichtfeld | Plan-Status | |---|---| | Root Cause | nicht zutreffend (Erweiterung des bestehenden MS-MTF-1) | | geänderte Dateien | 4 (data_fetcher.py, base_strategy.py, multi_strategy_runner.py, settings.py) | | Tests | 18 neu + Regression-Sweep | | Cross-Impact-Ergebnis | siehe §2 | | Runtime-Status | unverändert während Plan-Phase | | 3-Way MD5 | post-Cutover-Pflicht (4 Files) | | Bot/Worker/GUI Status | nur Bot betroffen (kein Worker, kein GUI) | | DB-Boundaries | metadata_json erweitert, kein Schema-Change | | Mainnet-Status | weiterhin verboten | | bewusst nicht geändert | position_manager.py, live_trade.py, trader_core.py, CommandBus, GUI, Strategy-Param, Risk-Cap, Exit-Toolkit (BE/Partial/Trailing/Hold), `_is_4h_confirming`, Strategy-Subklassen | | verbleibende Risiken | §11 | | Follow-up-Backlog | MS-MTF-CACHE-PERSIST-1 (P3), post-Shadow Re-Eval, MS-Live-Pilot-Plan, BTC-Macro, Regime-Aware-Params, Loss-Streak-Circuit-Breaker, Strategy-Loss-Backtest-Audit (paralleler P1-Track) | --- ## STOP Plan abgeschlossen. **Keine Umsetzung.** Operator-Entscheidung zu Q1–Q9 + `GO EXECUTE MS-MTF-D-SHADOW-LOGGER-1` erforderlich für Code-Phase.