# MS-MTF-D-PROTECTED-SHADOW-PLAN — Plan-Only **Datum:** 2026-06-05 (~00:35 UTC) **Modus:** PLAN-ONLY · 0× Code · 0× Push · 0× Orders · 0× Runtime-Apply **Walk-Forward-validierte Basis-Konfig:** 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. Logge die neue MTF-D-Entscheidung neben der bestehenden, ohne die Live-Pipeline zu blockieren. Sammle 2–4 Wochen Vergleichsdaten für Pilot-Entscheidung. **Boundaries:** Bot bleibt im `MULTI_STRATEGY_DRY_RUN=true`. Kein Position-Manager-Parameter-Touch. Keine Execution-Logik-Änderung. --- ## 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()` | trend_follow, breakout, mean_reversion, vwap, volatility_sweep, oversold_bounce | Tests pro Strategy ergänzen | | `data_fetcher.py` | 2 neue cached fetcher: `fetch_ohlcv_8h_cached`, `fetch_ohlcv_12h_cached` | nur MS-Runner | Cache-TTL aligned mit 4h-Cache (15 min) | | `multi_strategy_runner.py` | holt 8h+12h vor strategy-loop, passt durch | MS-Runner-Tests | df_8h / df_12h kwargs | | `multi_strategy_runner.py` | erweitert `emit_decision()` metadata mit `mtf_shadow` | `decision_logs` table | metadata_json bekommt neue Keys, KEIN DB-Migration nötig | | `multi_strategy.log` JSONL | neue Felder | Backtest-Tools | abwärtskompatibel | | `position_manager.py` | **0** | – | – | | `live_trade.py`, `trader_core.py` | **0** | – | – | | GUI, CommandBus, DB-Schema, env-files | **0** | – | – | **Mainnet-Status:** unverändert verboten. **MS-Live-Status:** unverändert DRY_RUN. --- ## 3. Geänderte Dateien (Plan-Spec) ### 3.1 `trading/scanner/data_fetcher.py` — 2 neue Cached Fetcher ```python def fetch_ohlcv_8h_cached(self, symbol, exchange_id='binance') -> Optional[pd.DataFrame]: """MS-MTF-D-SHADOW (2026-06-05) — 8h Cache, TTL 15 min.""" # identische Struktur wie fetch_ohlcv_4h_cached ... def fetch_ohlcv_12h_cached(self, symbol, exchange_id='binance') -> Optional[pd.DataFrame]: """MS-MTF-D-SHADOW (2026-06-05) — 12h Cache, TTL 15 min.""" ... ``` Plus 2 neue cache-Dicts 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 (Copy-Paste der existierenden `fetch_ohlcv_4h_cached`-Struktur). ### 3.2 `trading/strategies/base_strategy.py` — Shadow-Helper ```python # ── MS-MTF-D-SHADOW-1 (2026-06-05): 3-of-4 Multi-Timeframe Shadow ───── def _compute_mtf_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. Returns Dict mit: bullish_1h, bullish_4h, bullish_8h, bullish_12h (bool) score_1h, score_4h, score_8h, score_12h (int 0..100) bullish_count (int 0..4) mtf_d_pass (bool) = bullish_count >= 3 shadow_reject_reason (str | None) Bullish-Score je TF: +35 wenn close > EMA20 +35 wenn EMA20 > EMA50 +30 wenn close > EMA50 bullish = score >= 50 NIEMALS verwendet zur Blockade. Nur log/audit. """ def _score_one(df): if df is None or len(df) < 50: return (None, None) 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) except Exception: return (None, None) s1, b1 = _score_one(df_1h) s4, b4 = _score_one(df_4h) s8, b8 = _score_one(df_8h) s12, b12 = _score_one(df_12h) bullish_count = sum(1 for b in (b1, b4, b8, b12) if b is True) mtf_d_pass = bullish_count >= 3 reject = None if mtf_d_pass else f"mtf_d_only_{bullish_count}_of_4_bullish" 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, 'bullish_count': bullish_count, 'mtf_d_pass': mtf_d_pass, 'shadow_reject_reason': reject, } ``` **LoC:** ~50. **Wichtig:** Die existierende `_is_4h_confirming` Methode bleibt **unverändert** und wird weiterhin als Live-Gate genutzt. Die neue `_compute_mtf_shadow` ist rein Audit/Shadow. ### 3.3 `trading/strategies/multi_strategy_runner.py` — Fetch + Forward In `_evaluate_symbol`, nach dem existierenden `df_4h`-Fetch: ```python # MS-MTF-D-SHADOW-1 (2026-06-05): zusaetzlich 8h + 12h fuer Shadow-Audit. df_8h = None df_12h = None 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}), shadow null") ``` In der per-strategy loop, nach `strategy_result = strategy.score_signal(...)`: ```python # Shadow: berechne MTF-D Audit (nur Logging, KEIN Blocking) mtf_shadow = strategy._compute_mtf_shadow(df, df_4h, df_8h, df_12h) ``` 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. ### 3.4 Tests — `trading/tests/test_ms_mtf_d_shadow.py` (NEU) Mindestens 15 Tests: 1. Empty df → score None 2. Bullish strong (alle 4 TF) → mtf_d_pass=True, count=4 3. 3-of-4 bullish → pass=True, count=3 4. 2-of-4 bullish → pass=False, count=2, reject_reason set 5. 0-of-4 bullish → pass=False, count=0 6. Mixed scores (50, 49, 100, 100) → 3 bullish (49 not enough) 7. df_8h fetcher returns None defensive 8. df_12h fetcher returns None defensive 9. Cache TTL respected (smoke) 10. shadow NEVER blocks: even with mtf_d_pass=False, decision goes through 11. JSONL record contains mtf_shadow key 12. emit_decision metadata contains mtf_shadow 13. Backward compat: existing _is_4h_confirming unchanged 14. Existing trend_follow tests pass (no regression) 15. Existing breakout tests pass (no regression) Plus Regression-Sweep: - 20 SyncSanity - 20 ExitReasonFix - 20 MS-Dedup - 12 Stablecoin - 32 RECON-1 - 15 MS-MTF-1 (existing 4h gate) ### 3.5 `gui/docs/roadmap/ROADMAP.php` — neuer Eintrag (separater Commit) ```php [ 'id' => 'MS-MTF-D-PROTECTED-SHADOW-1', 'title' => 'MS Multi-Timeframe-D (3-of-4) Shadow Logger (LIVE since YYYY-MM-DD)', ... ] ``` --- ## 4. 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 | (in apply_trailing) | unverändert (2.0 % MFE implizit) | | 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 | --- ## 5. Shadow-Logging-Schema Jedes Decision-Log (`multi_strategy.log` JSONL + `decision_logs` table) bekommt zusätzlich: ```json { "ts": "2026-06-05T00:42:00Z", "symbol": "TON/USDT", "strategy_id": "trend_follow", "regime": "STRONG_TREND", "final_score": 8.45, "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, "bullish_count": 3, "mtf_d_pass": true, "shadow_reject_reason": null, "live_mtf_1_pass": true } } ``` `live_mtf_1_pass` (true/false aus existierendem `_is_4h_confirming`) ermöglicht Side-by-Side-Vergleich. --- ## 6. Optional Shadow-A/B (0.7/0.7 als Comparator) **NICHT empfohlen** im ersten Cut. Der Backtest hat 0.7/0.7 als „more robust" gezeigt aber Performance-Edge nur −20 pp vs 0.8/0.8. Für Shadow-Phase nur Haupt-Konfig 0.8/0.8 tracken. Wenn Operator explizit will: Backtest-only (kein Live-Toggle), separat als read-only-Audit-Phase nach Shadow-Period. --- ## 7. Tests ### Neue Tests (~15) - siehe §3.4 ### Bestehende Tests die grün bleiben müssen - `test_ms_mtf_confirmation.py` — existing 4h gate (15 Tests) - `test_ms_candidate_dedup_1.py` (20) - `test_ms_stablecoin_block_1.py` (12) - `test_state_cross_process_reconcile_1_1.py` (22) - `test_state_observability_1_4.py` (10) - `test_sync_balance_sanity_1.py` (20) - `test_exit_reason_fix_1.py` (20) - `test_phase_n7.py`, `test_phase_n8.py` (cumulative) - `test_data_link_1.py`, `test_data_link_1_fu2.py` - `test_t_split_2_emitter_wiring.py` **Erwartetes Total:** ~250 grün, 1 pre-existing N8-failure unverändert. --- ## 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 Files: base_strategy.py, data_fetcher.py, multi_strategy_runner.py + 1 Test) | | 3 | `docker compose build clawbot` | | 4 | `docker compose up -d --force-recreate clawbot` (worker NICHT betroffen — keine Schema-Änderung im Position-Manager) | | 5 | 3-Way MD5: base_strategy.py + data_fetcher.py + multi_strategy_runner.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 | | 9 | DB-Verify: `decision_logs.metadata_json` mit `mtf_shadow` für post-cutover-Rows | | 10 | 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` in jeder neuen Decision | `multi_strategy.log` | ✓ | | 4h-Cache + 8h-Cache + 12h-Cache populated | DataFetcher state | ✓ | | 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 | ✓ | ### Phase B — Shadow-Period (≥ 2 Wochen, besser 4 Wochen) | Metric | SQL/jq | |---|---| | Anzahl TRADE_CANDIDATEs total | `count(*) where decision='TRADE_CANDIDATE'` | | Davon mtf_d_pass=true | `count(*) where mtf_shadow.mtf_d_pass=true` | | Davon mtf_d_pass=false | `count(*) where mtf_shadow.mtf_d_pass=false` | | Discrepanz Live-MTF-1 vs MTF-D | per-row diff | | Per-Strategy Shadow-Filter-Rate | groupby strategy_id | | Bullish-Count-Verteilung (0/1/2/3/4) | histogram | | Reject-Reason-Cluster | distinct mtf_shadow.shadow_reject_reason | ### Phase C — Post-Shadow Re-Eval (nach 2-4 Wochen) - Walk-Forward-Validation Re-Run mit neuen 4 Wochen Daten - 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 Shadow-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 | abgeleitet aus aktuellem Sample (5) | | 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 | | 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 verdoppelt API-Last (3×4h = ~3 Binance-Calls/Scan-Cycle) | Niedrig | 15 min Cache deckelt; 6 Calls/Scan × 25 Symbole = 150 Calls/Scan ≤ Binance 1200/min | | 2 | DataFetcher Cache-Eviction → Memory-Leak | Niedrig | identische Struktur wie existierendes 4h-Cache; gleiche Eviction-Pattern | | 3 | Shadow-Logic verändert Live-Verhalten versehentlich | Hoch (wenn fehlerhaft) | Strikte Read-Only Garantie: `_compute_mtf_shadow` returnt Dict, wird NIE auf decision/reject_reason gemapped. Test 10 prüft dies explizit. | | 4 | metadata_json wächst — DB-Performance | Niedrig | mtf_shadow ist ~200 Bytes/Row, JSONB-effizient | | 5 | Strategy-Skripte (trend_follow etc.) müssen nicht angepasst werden | Niedrig | shadow wird auf MS-Runner-Ebene berechnet, NICHT per Strategy. Strategy-Signatur unverändert. | | 6 | 4-Wochen Shadow ist lang | Mittel | Operator kann nach 2 Wochen schon Zwischen-Check (kein Akzeptanz-Verdict) | | 7 | Marktregime ändert sich → Shadow-Daten unrepräsentativ | Mittel | dokumentieren; Re-Eval mit aktualisiertem Markt | | 8 | `_data_fetcher` instance lifecycle | Niedrig | DataFetcher pro MS-Runner-Instanz, MS-Runner pro Scan-Cycle → 4h+8h+12h-Caches sind cycle-lokal (kein Leak) | --- ## 12. Rollback-Plan Wenn etwas schiefgeht: ### Soft-Rollback (Code-Bug, keine State-Corruption) 1. `git revert ` der MS-MTF-D-SHADOW-Ä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 (neuer Setting-Toggle als zusätzlicher Kill-Switch) 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 3 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 — als zusätzliche Sicherheit | | Q2 | Cache-TTL für 8h+12h? | **A** 15 min (identisch zu 4h) | | 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) | --- ## 14. Geschätzter Aufwand | Item | LoC | Tests | Aufwand | |---|---:|---:|---| | `data_fetcher.py` 8h+12h Cached | 50 | 4 | 30 min | | `base_strategy.py` `_compute_mtf_shadow` | 50 | 8 | 45 min | | `multi_strategy_runner.py` integration | 30 | 3 | 30 min | | `settings.py` Kill-Switch | 5 | 0 | 5 min | | Tests neu | – | 15 | 60 min | | Tests Regression-Sweep | 0 | – | 30 min | | Code-Review + Cutover-Prep | – | – | 30 min | | **TOTAL** | **~135** | **30 neu** | **~3 h** | Roadmap-Commit + Push als separate Phase. --- ## 15. Boundaries 0× Bot-Code-Touch · 0× Trading-State · 0× Orders · 0× MS-Live-Aktivierung · 0× Mainnet · 0× Env-Änderung (außer optional Kill-Switch in settings.py-Default) · 0× DB-Schema · 0× DB-Migration · 0× ConfigProfile-Apply · 0× Bot-Recreate (Plan-Phase) · 0× Worker-Recreate (Plan-Phase) · 0× Push · 0× Roadmap-Commit (Plan-Phase) · 0× Coin-Allowlist · 0× Coin-Denylist. --- ## 16. STOP-Bedingungen Plan **NICHT umsetzen** wenn: - Operator-GO fehlt - Bot oder Worker unhealthy - Aktiver State-Coherence-Conflict im laufenden Bot - Strategy-Loss-Backtest-Audit (P1 offen) nicht zuerst gemacht (XLM-Loss-Beispiel zeigt Bot hat noch Loss-Asymmetrie) - MS-Dry-Run-Anomalien in den letzten 24h --- ## 17. Plan-Closure (per CLAUDE.md §13) | Pflichtfeld | Plan-Status | |---|---| | Root Cause | nicht zutreffend (Erweiterung, kein Fix) | | geänderte Dateien | 3 (Plan-Spec in §3) | | Tests | 15 neu + Regression-Sweep | | Cross-Impact-Ergebnis | siehe §2 | | Runtime-Status | unverändert während Plan-Phase | | 3-Way MD5 | post-Cutover-Pflicht | | 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, env (außer Kill-Switch optional) | | verbleibende Risiken | §11 | | Follow-up-Backlog | post-Shadow Re-Eval, MS-Live-Pilot-Plan, BTC-Macro, Regime-Aware-Params, Loss-Streak-Circuit-Breaker | --- ## STOP Plan abgeschlossen. **Keine Umsetzung.** Operator-Entscheidung zu Q1–Q8 + `GO EXECUTE MS-MTF-D-PROTECTED-SHADOW-1` erforderlich für Code-Phase.