# T-SPLIT-PREFLIGHT — Read-only Architecture Audit **Phase:** T-SPLIT-PREFLIGHT — Strategiefluss in T1/T2/T3 splitten **Stand:** 2026-05-09 18:00 UTC **Vorbedingung:** G10 CLOSED (`c141d2d`) ✓ **Status:** Read-only Audit & Scope. **Kein Code, keine Migration, keine Mutation.** Lieferbericht ohne Implementierung. --- ## Kurz-Zusammenfassung Der aktuelle Bot trennt schon implizit zwei Welten: **Haupt-Bot (Binance, Multi-Strategy)** und **Tier 3 (Solana DEX, VolatilityHunter + Pump-Monitor)**. Die T-SPLIT-Taxonomie aus dem Prompt (T1=Standard, T2=Pump&Dump, T3=Copy-Trading) **mappt nicht 1:1 auf die heutige Tier-Struktur**: - Heute: "Tier 1+2" = ein zusammenhängender Multi-Strategy-Pfad → entspricht **T1** - Heute: "Tier 3" = VolatilityHunter + Pump-Detector + Wallet-Tracker → entspricht **T2** (Pump-Detection ist die dominante Funktion) - Neu: **T3 (Copy-Trading)** existiert als **deaktiviertes Skelett** im `tier3/wallet_tracker.py` (`COPY_TRADING_ENABLED=false`); echte Execution fehlt **Zentrale Befunde:** 1. **`strategy_id` Spalten existieren in allen relevanten Tabellen, sind aber NULL**: `decision_logs.strategy_id` 0/74336 distinct, `position_snapshots.strategy_id` 0/5210, `trade_logs.strategy_id` 0/9. Der Bot ruft die Emitter, übergibt aber `strategy_id` nicht. Kein Backfill auf T1/T2/T3 möglich — ALLE alten Daten werden zwingend `legacy_unknown`. 2. **G3 StrategyStatsBuilder benutzt schon `legacy_unknown`** als Fallback (`COALESCE(NULLIF(strategy_id, ''), 'legacy_unknown')`). Konsistent mit dem T-SPLIT-Default. 3. **Naming-Kollision:** "Tier" wird im Code für **zwei verschiedene Konzepte** benutzt: - **strategy-tier:** Tier1+2 vs Tier3 (Subsystem-Trennung Binance vs Solana) - **score-tier:** CORE-HIGH/CORE-MID/CORE-LOW/OPPORTUNISTIC (Position-Sizing nach final_score) T-SPLIT muss klar zwischen **strategy_group** (T1/T2/T3 Subsystem) und **score-tier** (Sizing-Bucket) unterscheiden. 4. **5 STRATEGY_REGISTRY-Einträge** (`trend_follow`, `breakout`, `mean_reversion`, `vwap_mean_reversion`, `volatility_sweep`) gehören alle zu T1. 5. **GUI hat bereits `strategy_id`-Filter** in DecisionLog/Position/Trade/Strategy-Stat-Resources — würde durch `strategy_group` ergänzt, nicht ersetzt. **Empfehlung: Option B+ → erst `strategy_group` Spalte (eine Migration), dann später bei Bedarf `strategy_type` + normalisierte `strategy_id`.** Pure-metadata_json (Option A) reicht NICHT für Filament-Filter und G3-Aggregator-Performance. --- ## 1. Aktueller Bot-Strategiefluss ### 1.1 Code-Map (Datei → Zeile → Funktion → Zweck → T-Zuordnung) | Datei | Zeile | Funktion / Symbol | Heutiger Zweck | T-Zuordnung | |---|---|---|---|---| | `trading/main.py` | 147 | `run_scan_cycle(...)` | Hauptloop, scan + entry-decisions auf Binance | **T1** | | `trading/main.py` | 1094 | `from strategies.volatility_hunter import VolatilityHunter` | dynamic import — **innerhalb des Haupt-Bot-Kontexts**, ZUSÄTZLICH zur regulären Strategie-Liste | T1 (legacy hunter), nicht Tier 3 (!) | | `trading/main.py` | 1180-1182 | `from tier3.loop import Tier3Loop; tier3 = Tier3Loop(reporter=reporter)` | initialisiert Solana-Subsystem | **T2** (Pump-Detection / Solana DEX) | | `trading/main.py` | 1211 | `tier3.pump_monitor.run()` | Pump.fun WebSocket Monitor (async) | **T2** | | `trading/main.py` | 1326-1335 | `if tier3: tier3.run() ... _process_t3_forwarded_signals(...)` | Tier3-Cycle + Forward signals von T2 zu Haupt-Bot | T2 → T1 Bridge | | `trading/main.py` | 1369-1371 | `tier3.check_exits_only()` | T2-Exits zwischen Cycles | T2 | | `trading/main.py` | 941 | `_process_t3_forwarded_signals(...)` | Übernimmt forwarded buy-signals von T2-Subsystem in T1-PaperTrader | Bridge T2→T1 | | `trading/strategies/registry.py` | 28-34 | `STRATEGY_REGISTRY` | Zentrale Map strategy_id → Klasse: `trend_follow`, `breakout`, `mean_reversion`, `vwap_mean_reversion`, `volatility_sweep` | **alle T1** | | `trading/strategies/multi_strategy_runner.py` | 248 | `run_multi_strategy(...)` | erzeugt Decisions je `(symbol, strategy_id)`-Kombination | T1 | | `trading/strategies/decision_engine.py` | — | `FinalDecisionEngine` | konsolidiert Strategy-Outputs zu Buy/Hold/Reject | T1 | | `trading/strategies/router.py` | — | Strategy-Router (regime → strategy_id) | wählt Strategie pro Regime aus | T1 | | `trading/strategies/volatility_hunter.py` | — | Legacy `VolatilityHunter` (Binance) | Score-basierter Hunter im Haupt-Bot | T1 (legacy, nicht Tier3!) | | `trading/tier3/loop.py` | — | `Tier3Loop` | Solana-Subsystem-Hauptschleife mit Signal-Aggregation (Gruppe A/B/C, Crosscheck-Bonus) | **T2** | | `trading/tier3/pump_fun_monitor.py` | — | `PumpFunMonitor` | Helius-WebSocket auf Solana Mainnet, BC>=85% / MigrateEvent | **T2** | | `trading/tier3/wallet_tracker.py` | 70 | `WalletTracker` (`COPY_TRADING_ENABLED` env) | Helius Wallet History — Smart-Wallet-Tracking | **T3** (deaktiviert) | | `trading/tier3/portfolio.py` | — | `tier3_portfolio.json` (isoliert) | T2-Portfolio | T2 | | `trading/tier3/price_fetcher.py` | — | Jupiter + Birdeye | DEX-Preise | T2 | | `trading/news/listing_detector.py` | 13 | Listing/Pump-Detector (8 Quellen) | wird vom T2-Tier3-Loop konsumiert | T2 | | `trading/news/listing_detector.py` | 816 | `_check_wallet_copytrading()` | nur aktiv wenn `COPY_TRADING_ENABLED=true` | T3 | ### 1.2 Buy-Signal-Pfade | Pfad | Datei:Zeile | Output | |---|---|---| | **T1 multi-strategy** | `multi_strategy_runner.py:248` | `decision = {strategy_id, action, score, ...}` (intern korrekt) | | **T1 legacy single-strategy** | `main.py` cycle (legacy direct path) | Decision ohne strategy_id-Felder | | **T2 Tier3-Forwarded** | `main.py:941 _process_t3_forwarded_signals` | übersetzt T2-Signal zu T1-PaperTrader-Buy | | **T2 Pump-Monitor direct** | `tier3/loop.py` + `pump_fun_monitor.py` | Tier3-eigener Trader (Jupiter Swap) — schreibt NICHT in `commands`/`decision_logs` | | **T3** | _existiert nicht_ — `wallet_tracker.py` ist Detector-Only, kein Order-Execution | n/a | ### 1.3 Reject-Erzeuger | Datei | Funktion | Reject-Reasons (Beispiele) | |---|---|---| | `trading/execution/safety_check.py` | `is_blocked_stable_or_peg_asset()` | `blocked_stablecoin_base_asset`, `blocked_suspected_peg_asset` | | `trading/execution/risk_manager.py` | `check_*` | Max-Open, Max-Risk-pct, Loss-Limits | | `trading/execution/multi_strategy_position_sizer.py` | `calculate_size_with_tier_caps()` | min_notional, exposure_cap, cash_cap | | `trading/strategies/multi_strategy_runner.py:215` | strategy_id-mismatch reject | `"Rejected: strategy_id mismatch"` | | `trading/main.py` `emit_decision(action='reject', ...)` | 6 Aufruforte | div. reject_reasons | ### 1.4 Position-/Trade-Schreibpfade | Funktion | Datei:Zeile | Schreibt in | |---|---|---| | `emit_position_snapshot(...)` | `main.py:292` | `position_snapshots` — `strategy_id=_pos.get('strategy_id')` (heute None weil State es nicht hat) | | `emit_trade(...)` | `main.py:261` | `trade_logs` — **kein strategy_id-Argument übergeben** | | `emit_decision(...)` | `main.py:384, 448, 499, 515, 535, 552, 566` (7 call sites) | `decision_logs` — **kein strategy_id-Argument übergeben** | | `emit_active_config(...)` | `command_worker.py:1570` | `bot_statuses.metadata_json` | | `emit_bot_status(...)` | `main.py` | `bot_statuses` | **Root cause**: 7 von 7 `emit_decision`-Aufrufstellen rufen ohne `strategy_id`-Parameter auf, obwohl `db_emitter.emit_decision` ihn akzeptiert. Selber Befund für `emit_trade`. Bei `emit_position_snapshot` wird `strategy_id` zwar weitergegeben, aber `state['positions'][symbol]['strategy_id']` ist meist None weil die Buy-Schreiber das Feld nie setzen. --- ## 2. Datenmodell prüfen ### 2.1 Tabellen-Befund | Tabelle | strategy_id-Spalte | Zeilen | Distinct strategy_id | Schema-Felder relevant | |---|---|---|---|---| | `decision_logs` | varchar(255) | 74336 | **0** (alle NULL) | profile_id, action, score, regime, base_regime, source, mode, environment, metadata_json | | `position_snapshots` | varchar(255) | 5210 | **0** | base/quote_asset, profile_id, decision_id, mode, environment, metadata_json | | `trade_logs` | varchar(255) | 9 | **0** | base/quote_asset, profile_id, decision_id, exit_reason, mode, environment, metadata_json | | `strategy_stats` | varchar(255), NOT NULL | 420 | **1** (`legacy_unknown`) | profile_id, environment, mode, period, period_start/end, symbol, regime, timeframe, score_bucket | | `config_profiles` | n/a | n | n/a | summary_json (G7 allowlist), version, environment, source, status, parent_profile_id | | `risk_settings` | n/a | n | n/a | config_profile_id (FK), key, value_text/json, min/max — **global, NICHT per strategy_group** | | `bot_statuses` | n/a | n | n/a | mode, environment, network, bot_state, metadata_json (active_runtime_config!) | | `commands` | n/a (G6.5) | n | n/a | command_type, payload_json, status, idempotency_key | ### 2.2 Beantwortung der Spec-Fragen - **Spalten für strategy_id:** ja, in 4 von 8 Tabellen (decision_logs, position_snapshots, trade_logs, strategy_stats). Aber leer. - **strategy_group / tier:** **NEIN, existiert nirgends.** "Tier" tauchen nur in Settings/runtime-config-Keys auf (`tier_low/mid/high_allocation_pct`) — das ist Score-Sizing-Tier, nicht Strategy-Group. - **metadata_json T1/T2/T3 indirekt:** möglich aber nicht standardisiert. `regime`, `score`, `strategy_id` (NULL) — die einzigen vorhandenen Felder, die ein Mapping erlauben würden. - **Neue Spalten nötig?** **JA**, zumindest `strategy_group`. Filament-SelectFilter + G3-GROUP-BY brauchen indizierte Spalten; metadata_json würde Aggregation langsam machen. - **metadata_json kurzfristig:** unzureichend für Tabellen mit 74k+ Zeilen. ### 2.3 Optionsbewertung | Option | Bewertung | |---|---| | **A** Nur metadata_json | ❌ ungeeignet für Filament-Filter (`SelectFilter` braucht indizierte Spalte) und für G3 (`GROUP BY`-Performance bei json->>'strategy_group' ist schlecht) | | **B** Spalte `strategy_group` (varchar) | ✅ minimaler Migration-Footprint, Filament-Filter-ready, G3-erweiterbar | | **C** Drei Spalten `strategy_group` + `strategy_type` + normalisierte `strategy_id` | ✅✅ langfristig sauber, aber Doppelmigration und mehr Drift-Surface | **Empfehlung:** **B als T-SPLIT-1 jetzt, C als optional T-SPLIT-1b später** (wenn der Bedarf für strategy_type-Trennung in der Praxis auftritt). `strategy_id` bleibt erstmal als-is (varchar, NULL erlaubt; der bestehende `legacy_unknown`-Fallback in G3 fängt das). --- ## 3. Zielmodell ### 3.1 Vorgeschlagenes Schema ```text strategy_group ENUM-like varchar [T1, T2, T3, legacy_unknown] strategy_type ENUM-like varchar [standard, pump_dump, copy_trading, legacy_unknown] strategy_id varchar (heute schon da, bleibt) T1: trend_follow, breakout, mean_reversion, vwap_mean_reversion, volatility_sweep, volatility_hunter_legacy T2: tier3_volatility_hunter, pump_fun_monitor, listing_detector, birdeye_signal, binance_listing_announce, … T3: copy_trading_ (placeholder bis Execution gebaut) ``` ### 3.2 Beispiel-Tripel | strategy_group | strategy_type | strategy_id | |---|---|---| | T1 | standard | trend_follow | | T1 | standard | breakout | | T1 | standard | mean_reversion | | T1 | standard | vwap_mean_reversion | | T1 | standard | volatility_sweep | | T1 | standard | volatility_hunter_legacy | | T2 | pump_dump | tier3_volatility_hunter | | T2 | pump_dump | pump_fun_monitor | | T2 | pump_dump | listing_detector | | T3 | copy_trading | copy_source_placeholder | ### 3.3 Validierung / Erlaubte Werte - **String, nicht Postgres-ENUM.** Begründung: ENUM-ALTER ist DDL-schwer; varchar mit CHECK-Constraint ist flexibler und schon der etablierte Bot-Pattern (siehe `config_profiles.status`, `commands.command_type`). - **Allowlist-Validation in einem Service** (`StrategyTaxonomy.php` analog zu `Phase1RiskAllowlist`). - **Unbekannter Wert → REJECT auf bot-side bei emit (warning log + null-fallback) + REJECT auf gui-side bei filter (404).** ### 3.4 Migration & Backfill - **Migration:** ADD COLUMN `strategy_group` varchar(32) NULL + index. Reichen aus für T-SPLIT-1. - **Backfill für alte Daten:** **NICHT auf T1.** Alte Zeilen kriegen `legacy_unknown` (entweder explizit per UPDATE oder via Default-Constant + COALESCE). Begründung: bei den 74336 alten decision_logs lässt sich nicht zuverlässig sagen, ob ein Pfad damals T1 oder T2 war (T3 kann nicht dabei sein, da Copy-Trading nicht aktiv war). - **Default für neue Daten:** Bot-Emitter MUSS strategy_group setzen — sonst NULL → legacy_unknown via COALESCE. - **G3 Aggregator:** `legacy_unknown` ist bereits berücksichtigt; nur `strategy_group` als zusätzliche GROUP-BY-Dimension einführen. ### 3.5 Sicherheits-Default > **Alte Daten = `legacy_unknown`.** Niemals blind als T1 markieren. Begründung: bei 5210 position_snapshots und 74336 decision_logs ist nicht beweisbar, welcher Pfad damals aktiv war. `legacy_unknown` zerstört keine forensische Information. --- ## 4. GUI-Struktur Vorschlag ### 4.1 Aktuelle Filament-Surface (Befund) | Resource | strategy_id-Spalte heute | strategy_id-Filter heute | |---|---|---| | `DecisionLogResource` (`gui/app/Filament/Resources/DecisionLogResource.php`) | ja (line 71) | ja `SelectFilter::make('strategy_id')` (line 79) | | `PositionSnapshotResource` | ja (line 74) | ja (line 91) | | `TradeLogResource` | ja (line 67) | ja (line 79) | | `StrategyStatResource` | ja (line 59) | ja (line 73) | | `MonitoringDashboard` (Page) | nein | nein | | `RuntimeConfigStatusWidget` (G10-6.1) | nein | nein | | `ConfigProfileResource` | nein (eigene Achse) | nein | ### 4.2 Vorschlag pro Surface **Dashboard:** - Drei Cards "T1 Standard Trading", "T2 Pump & Dump", "T3 Copy Trading (planned)" mit Per-Group-KPIs (open positions, exposure, total_pnl, last decision) - T3 als `disabled` / `planned`-Badge bis Execution gebaut **Decision Logs:** - Neue Spalte `strategy_group` mit Badge (T1=blue, T2=amber, T3=slate) - `SelectFilter::make('strategy_group')` zusätzlich zum bestehenden strategy_id-Filter - Default Tab: `T1` (oder "All") **Position Snapshots:** - Spalte + Filter + Group-Default `strategy_group` **Trade Logs:** - Spalte + Filter + Group-Default `strategy_group` - PnL-Aggregat-Widget je strategy_group **Strategy Stats:** - Tabs `T1 / T2 / T3 / All` (analog G3-v2 Period-Tabs) - TopActivity-Widget filtert pro strategy_group **Config Profiles:** - Optional summary_json-Sektionen `t1` / `t2` / `t3` — siehe §8 ### 4.3 Read-only vs Mutation | Änderung | Migration nötig | Bot-Emitter-Anpassung nötig | |---|---|---| | Neue Spalten + Indexe | **JA** (T-SPLIT-1) | nein | | Filament-Filter/Tabs/Cards | nein (sobald Spalte da ist) | nein | | Bot setzt strategy_group bei emit | nein | **JA** (T-SPLIT-2) | | Backfill `legacy_unknown` | nein (per COALESCE im Reader) ODER ja (per UPDATE) | nein | | GUI-Cards zeigen Per-Group-KPIs | nein | nein (Reader wertet GROUP BY aus) | --- ## 5. G3 Strategy Stats Auswirkung ### 5.1 Heute `StrategyStatsBuilder.php` gruppiert (lines 65-84, 119-147, 206-216) nach **(strategy_id, profile_id, symbol, regime, environment) + period + score_bucket**. Verwendet `COALESCE(NULLIF(strategy_id, ''), 'legacy_unknown')`. ### 5.2 Nach T-SPLIT - `strategy_group` als **7. (oder als ERSTE) GROUP-BY-Dimension** einbauen - Composite-Index `(strategy_group, strategy_id, profile_id, symbol, regime)` vorbereiten - Score-Buckets je strategy_group können später getrennt werden (T2 hat andere Score-Verteilung als T1, deswegen kein gemeinsamer Bucket-Name) - Backfill: NEIN — neuer Aggregator-Lauf ab Cutoff-Date, vorhandene strategy_stats behalten ihre alte Form mit `strategy_group=legacy_unknown` ### 5.3 Risiko - Row-Count multipliziert sich um Faktor ~1 (heute fast alle legacy_unknown, wird sich ändern wenn Bot strategy_group setzt) → langfristig +30 % Zeilen je Periode - Index-Cost: `(strategy_group, ...)` als zusätzlicher Composite-Index ~ +50 MB bei aktueller Tabellengröße — vernachlässigbar - G3-Scheduler (15-min/hourly/6-hourly) bleibt unverändert ### 5.4 Empfehlung `strategy_group` in Aggregator-Schlüssel aufnehmen. Backfill nur für neue Daten ab Migration-Datum. --- ## 6. T2 Pump & Dump — Befund ### 6.1 Existiert bereits - `trading/tier3/` — vollständiges Solana-Subsystem - `trading/tier3/pump_fun_monitor.py` — Helius WebSocket auf Pump.fun-Programm - `trading/news/listing_detector.py` — 8 Signal-Quellen (Birdeye, Binance ExchangeInfo, CoinGecko, @binanceannouncements, DEX Screener, Reddit RSS — Twitter/CryptoPanic deaktiviert) - `trading/tier3/loop.py` — Signal-Aggregation Gruppe A/B/C mit Crosscheck-Bonus - Eigenes Portfolio: `logs/tier3_portfolio.json` (NICHT `live_portfolio.json`) - Eigene Trader-Schicht: Jupiter Swap (DEX), nicht ccxt ### 6.2 Reject-Reasons spezifisch zu T2 | Reason | Source | |---|---| | BC-Threshold zu niedrig | `pump_fun_monitor.py` (BC < 85%) | | Confidence-Cap (Reddit max 0.45) | `listing_detector.py` | | Crosscheck fehlt zwischen Gruppen | `tier3/loop.py` | | BNB-Chain ausgeschlossen (durable rule) | `listing_detector.py` | ### 6.3 Risk-Settings T2-spezifisch Heute alles in `state['tier3_*']` gehalten: - `tier3_budget` — eigenes Budget - `tier3_used` — Tracking - `tier3_positions` — separate Positions → T2 hat schon **eigene** Max-Exposure / Max-Position-Allocation, aber als JSON-State, nicht als G9-Risk-Settings. ### 6.4 Vorschlag - T2 weiter im isolierten tier3-Subsystem belassen (kein Merge mit T1) - `strategy_group=T2` an die existierende emit-Pipeline (sobald Bot strategy_group emittiert) - T2-spezifische Risk-Settings später als G9 Phase-2-Allowlist (`max_pump_exposure_pct`, `pump_cooldown_seconds`, `min_bc_pct`) — eigene Phase, nicht in T-SPLIT-PREFLIGHT mischen **Keine neue T2-Logik in T-SPLIT-PREFLIGHT.** Nur Scope. --- ## 7. T3 Copy Trading — Befund ### 7.1 Existiert teilweise - `trading/tier3/wallet_tracker.py` — **Detector-Skelett**: Helius Wallet History API, Smart-Wallet-Liste, Activity-Monitoring - ENV-Schalter: `COPY_TRADING_ENABLED=false` (durable deaktiviert) - **Keine Order-Execution.** Nichts in `multi_strategy_runner.py` oder `tier3/loop.py` triggert Buys aus Wallet-Aktivität - Kein Datenmodell für Copy-Source / Leader / Signal-Feed ### 7.2 Risiken die T3 später braucht (NICHT in T-SPLIT bauen) | Risk | Begründung | |---|---| | `max_copy_allocation` | Begrenzt Gesamteinsatz pro Copy-Source | | `copy_source_allowlist` | Whitelist von Wallet-Adressen / Source-IDs | | `max_slippage` | DEX-Swaps haben Slippage-Risiko | | `copy_delay_seconds` | Delay zwischen Source-Trade und Copy-Trade | | `manual_kill_switch` | Sofort-Abschaltung pro Source | | `audit_trail` | Vollständige Source→Copy-Trade-Verkettung | ### 7.3 Vorschlag - T3 in der GUI als **`planned/disabled`** sichtbar (Card grau, Tooltip "Copy Trading is planned — enable via T3-EXECUTION phase") - `strategy_group=T3` reservieren, aber Bot emittiert kein T3 bis Execution gebaut ist - Eigene Phase `T3-EXECUTION` (NACH T-SPLIT, eigener Preflight) mit Source-Modell, Order-Path, Audit-Trail, Risk-Settings, manueller Kill-Switch - **Keine externen API-Calls in T-SPLIT.** Keine Order-Logik. Pure Reservierung. --- ## 8. Config / Risk Auswirkung ### 8.1 G9 Risk Settings — global vs. per-group? **Aktuell:** Risk-Settings sind global (eine `risk_settings`-Tabelle, ein Set Werte pro `config_profile_id`). **Optionen:** | Option | Pro | Kontra | |---|---|---| | **Global lassen** | Einfachster Migration-Footprint; G10 Apply-Pfad bleibt unverändert | Operator kann nicht T2-Pump-Settings unabhängig von T1 tunen | | **Pro-Group `risk_settings.strategy_group`** | T1/T2 unabhängig riskbar | Migration-Heavy; G9-Allowlist + G10-Apply müssten pro-Group erweitern | | **Hybrid: globale Settings + T2-spezifische Sektionen in summary_json** | mittlerer Footprint | leicht inkonsistent; zwei Quellen der Wahrheit | **Empfehlung:** Hybrid. T1 bleibt G9 wie heute. T2-spezifische Settings (sobald gebraucht) in `config_profiles.summary_json.t2` als eigene Sub-Sektion. T3-Settings analog. Diese Sub-Sektionen sind separate G9-Phase-2-Allowlists, nicht in T-SPLIT-PREFLIGHT-Scope. ### 8.2 ConfigProfile summary_json — Sektionen? **Heute:** flach, ein gemeinsamer Key-Space (G7-Allowlist). **Nach T-SPLIT:** könnte um Sektionen erweitert werden: ```json { "log_level": "info", "decision_log_verbosity": "full", "t1": { "max_open_positions": 4, ... }, "t2": { "max_pump_exposure_pct": 0.05, "min_bc_pct": 85, ... }, "t3": { "copy_source_allowlist": ["..."], ... } } ``` **Risiko:** G7-Allowlist müsste erweitern, G10-Apply-Pfad müsste sektioniert. Beides nicht trivial. **Empfehlung:** **Phase-1 nicht ändern.** summary_json bleibt flach. T2/T3-Sektionen kommen wenn die Phase tatsächlich Bot-Side-Settings braucht (`T-SPLIT-5/6`). ### 8.3 Separate Config-Profile pro strategy_group? Möglich (`config_profiles.strategy_group` Spalte), aber overkill für T-SPLIT-1. Lieber `config_profiles.summary_json.tN` Sektionen, später wenn nötig. ### 8.4 Apply-Pfad G10-4.2 RuntimeConfigWriter hat heute eine fixe summary/risk-Allowlist. Pro-Group-Apply würde: - separate `apply_profile_t2` / `apply_profile_t3` Command-Types ODER - erweitern `apply_profile_testnet` mit `target_group` payload-Feld **Empfehlung:** in T-SPLIT-PREFLIGHT NICHT entscheiden. G10-Apply bleibt für T1. T2/T3-Apply ist eigene Phase. --- ## 9. Migration / Backfill / Compatibility ### 9.1 Notwendige Migrationen | Migration | Tabelle | Spalten | T-SPLIT-Phase | |---|---|---|---| | 1 | `decision_logs` | `+strategy_group varchar(32) NULL` + Index | T-SPLIT-1 | | 2 | `position_snapshots` | analog | T-SPLIT-1 | | 3 | `trade_logs` | analog | T-SPLIT-1 | | 4 | `strategy_stats` | analog | T-SPLIT-1 | | 5 (optional) | dieselben | `+strategy_type varchar(32) NULL` | T-SPLIT-1b | | 6 (T2-spez.) | `risk_settings` | `+strategy_group varchar(32) NULL` (Nullable!) | T-SPLIT-5 | ### 9.2 Backfill | Tabelle | Strategy | Begründung | |---|---|---| | `decision_logs` 74336 rows | UPDATE → `legacy_unknown` ODER per COALESCE im Reader | nicht beweisbar welcher Pfad | | `position_snapshots` 5210 rows | analog | analog | | `trade_logs` 9 rows | analog (kann manuell `legacy_unknown` setzen) | klein, manuell ok | | `strategy_stats` 420 rows | UPDATE → `legacy_unknown` | konsistent mit existing G3 logic | **Sicherheits-Empfehlung:** **per COALESCE im Reader/Aggregator** statt UPDATE — dann ist die Migration reversibel und keine bestehenden Zeilen werden gemutet. Spalte default NULL, Reader macht `COALESCE(strategy_group, 'legacy_unknown')`. ### 9.3 Compatibility - Bestehende Filament-Resources (DecisionLog/Position/Trade/Strategy-Stat) zeigen automatisch alte Zeilen als `legacy_unknown`. - Bestehende G3-Aggregation zeigt `legacy_unknown` (schon heute). - Bestehende Bot-Cycles emit'ten ohne strategy_group → nullable. - G10 Apply/Clear bleibt unberührt (G10 schreibt risk_settings/summary_json, nicht decision_logs). ### 9.4 Klare Empfehlung Alte Daten = `legacy_unknown` per COALESCE-Fallback im Reader, **kein UPDATE**. Neue Daten ab T-SPLIT-2 = sauber T1/T2/T3. --- ## 10. Phasenplan ### 10.1 Vorschlag konservativ | Phase | Inhalt | Migration | Bot-Code | GUI-Code | Risiko | |---|---|---|---|---|---| | **T-SPLIT-1** | Schema: `strategy_group` Spalte + Index in 4 Tabellen | ✅ | nein | nein | gering | | **T-SPLIT-1b** *(optional)* | Schema: `strategy_type` Spalte | ✅ | nein | nein | gering | | **T-SPLIT-2** | Bot-Emitter setzt `strategy_group` (+ optional `strategy_type`, `strategy_id`) für T1, T2; T3 NICHT | nein | ✅ | nein | mittel (multi-emit-paths anfassen) | | **T-SPLIT-3** | GUI Filter + Tabs + Cards + Badges (DecisionLog/Position/Trade/StrategyStat/Dashboard) | nein | nein | ✅ | gering | | **T-SPLIT-4** | G3 StrategyStatsBuilder erweitert um `strategy_group` GROUP BY | nein | nein (PHP-only) | ✅ | gering | | **T-SPLIT-5** *(separat, nach Bedarf)* | T2 spezifische Risk-Settings + summary_json.t2 + G9-Phase-2-Allowlist | ✅ | ✅ | ✅ | hoch | | **T-SPLIT-6** *(separat, nach Bedarf)* | T3 Copy-Trading Datenmodell + Source-Whitelist + Kill-Switch UI; Execution kommt in eigener T3-EXECUTION-Phase | ✅ | nein in T-SPLIT (nur Scope; Execution = T3-EXECUTION) | ✅ (nur planned/disabled UI) | mittel | ### 10.2 Risiken pro Phase **T-SPLIT-1:** - Migration auf 79k+ Zeilen — sub-second mit Postgres ADD COLUMN NULL. - Index-Build: ~3-5 Sekunden auf 74k decision_logs. - DB-Backup PFLICHT vor Migration (durable rule per `feedback_backup_before_live_actions.md`). **T-SPLIT-2:** - Anfassen aller 7 emit_decision-Aufrufe + emit_trade + position-State-Schreiber. Risiko: nicht alle Pfade decken → strategy_group bleibt teilweise NULL → COALESCE-Fallback rettet aber unsauber. - Bot-Restart NÖTIG damit Emit-Code aktiv wird. - Test-Surface: einer pro Pfad (multi_strategy_runner, tier3/loop, legacy single-strategy fallback). **T-SPLIT-3:** - Reine Filament-Layer. Risiko: Filament v4 Tab-Filtering hat den `$query` parameter-name Caveat (G3-v2 lesson). **T-SPLIT-4:** - Aggregator-Run muss neu starten; existing strategy_stats bleiben mit `strategy_group=legacy_unknown`. - G3-Scheduler (15-min/hourly/6-hourly) muss nicht neu konfiguriert werden. **T-SPLIT-5:** - G9-Phase-2-Allowlist erweitern. Risiko: Cross-field-validation muss verstanden werden. - Apply-Pfad muss entscheiden, ob `apply_profile_testnet` pro-Group differenziert oder ob es eigener Command-Type wird. **T-SPLIT-6:** - Source-Modell + Audit-Trail braucht eigenen Preflight. ### 10.3 Tests | Phase | Tests | |---|---| | 1 | Migration up+down, index existiert, COALESCE im Reader gibt `legacy_unknown` | | 1b | analog für strategy_type | | 2 | je emit-Pfad ein Unit-Test der strategy_group setzt; Multi-Strategy-Runner Integration; tier3-loop Integration | | 3 | Filament Resource visibility, Filter, Badge, Default-Tab; Boundary-AST keine Mutation | | 4 | G3-Aggregator-Lauf mit gemischten T1/T2/legacy_unknown Daten; backwards-compat strategy_stats | | 5 | G9-Phase-2-Allowlist-Tests; cross-field-validation; G10-Apply-Pfad-Erweiterung | | 6 | Source-Modell-CRUD; Kill-Switch-Audit; **NICHT Execution** | ### 10.4 Reihenfolge-Empfehlung ``` T-SPLIT-1 (Schema) ↓ T-SPLIT-3 (GUI Filter — read-only auf legacy_unknown bis T-SPLIT-2) ↓ T-SPLIT-2 (Bot-Emitter-Setzung + Restart) ↓ T-SPLIT-4 (G3 erweitern) ↓ T-SPLIT-5/6 (separat, nach Bedarf, eigene Preflights) ``` T-SPLIT-3 vor T-SPLIT-2 ist bewusst: GUI ist sofort sichtbar nutzbar (alles `legacy_unknown`), Bot-Restart kommt erst danach kontrolliert. --- ## 11. Offene Fragen für User-Entscheidung | ID | Frage | Default-Vorschlag | |---|---|---| | **TQ-1** | strategy_group als varchar oder ENUM? | varchar mit CHECK-Constraint (flexibler, etablierter Pattern) | | **TQ-2** | strategy_type in T-SPLIT-1 oder als 1b? | als 1b separieren (kleinerer Migration-Footprint, T-SPLIT-1 sicher) | | **TQ-3** | Backfill via UPDATE oder per COALESCE-Fallback? | COALESCE-Fallback (reversibel, keine Mutation alter Zeilen) | | **TQ-4** | T2-Risk-Settings global oder per-group? | globale + summary_json.t2 Sektion (Hybrid; Phase 5) | | **TQ-5** | T3-Card im Dashboard sofort als planned/disabled oder erst nach T3-EXECUTION-Phase? | sofort als planned/disabled (Operator-UX-Konsistenz) | | **TQ-6** | Phase-Reihenfolge T-SPLIT-1 → 3 → 2 → 4 oder 1 → 2 → 3 → 4? | 1 → 3 → 2 → 4 (GUI sichtbar bevor Bot-Restart) | | **TQ-7** | tier3 Subsystem in Bot-Code umbenennen zu "t2"? | NEIN in T-SPLIT-Phase. Tier3-Naming bleibt als legacy alias; nur emit-Layer setzt strategy_group="T2". Naming-Cleanup ist eigene followup-Phase. | | **TQ-8** | "volatility_hunter" im Haupt-Bot (T1) und "tier3_volatility_hunter" in T2 — Naming-Kollision riskant? | strategy_id für legacy: `volatility_hunter_legacy`; strategy_id für T2: `tier3_volatility_hunter` — eindeutig | | **TQ-9** | Apply-Pfad pro Group (T-SPLIT-5)? | NICHT in T-SPLIT-PREFLIGHT entscheiden. G10-Apply für T1 unverändert. | | **TQ-10** | Backfill der bestehenden 9 trade_logs manuell auf `legacy_unknown`? | nein — COALESCE reicht. | --- ## 12. Stop-Regeln (für T-SPLIT-Implementations-Phasen, nicht für Preflight) Sofort stoppen + berichten wenn: - Bot-PID wechselt während emit-Pfad-Migration (T-SPLIT-2) - runtime_config.json mutiert während T-SPLIT-Phase (sollte gar nicht passieren) - live_portfolio.json mutiert - ccxt-Calls aus T-SPLIT-Code-Pfaden - Mainnet-Pfad sichtbar - DB-Migration produziert NULL-Constraint-Verletzung - G10 Apply/Clear-Tests brechen - G3-Aggregator-Lauf wird signifikant langsamer (>2× baseline) --- ## 13. Bestätigung — Read-only | Bedingung | Status | |---|---| | Kein Code geändert | ✅ | | Keine Migration durchgeführt | ✅ | | Kein Bot-Restart | ✅ Bot PID 4246 stable | | Keine Trading-Logik berührt | ✅ | | Keine Copy-Trading-Orders | ✅ (deaktiviert in Code, nicht angefasst) | | Keine Orders | ✅ | | Kein Mainnet | ✅ BINANCE_TESTNET=true | | Kein G10-Touch | ✅ G10 closed seit `c141d2d` | | Kein Worker | ✅ kein clawbot-worker daemon | | Keine .env-Mutation | ✅ mtime 1777991334 unverändert | | Kein Push | ✅ | **runtime_config.json:** absent (G10-5b state seit 2026-05-09 13:40:49 UTC) **git HEAD:** `c141d2d` (G10-6.4 closure) --- ## 14. Lieferbericht-Zusammenfassung - **Audit-Phase:** read-only, kein Code, keine Mutation - **Strategy-Map:** 5 Strategien in T1 (registry) + 1 legacy hunter (T1, separater Pfad) + tier3-Subsystem (= T2: pump_fun + listing_detector + Solana DEX) + COPY_TRADING-Skelett (= T3, deaktiviert) - **Datenmodell-Befund:** strategy_id existiert in 4 Tabellen aber NULL in 79551 Zeilen; strategy_group existiert nirgends - **GUI-Befund:** alle 4 Resources haben strategy_id-Filter; Dashboard hat keinen Group-Aggregat-View - **G3-Befund:** legacy_unknown Fallback schon implementiert; strategy_group als zusätzliche GROUP-BY-Dimension einfach erweiterbar - **T2-Befund:** existiert vollständig als tier3-Subsystem mit eigenem Portfolio + Pump-Monitor; muss nur als `strategy_group=T2` getaggt werden - **T3-Befund:** Wallet-Tracker-Detector-Skelett vorhanden, Execution fehlt komplett; T3-EXECUTION ist eigene Folgephase - **Zielmodell:** strategy_group + strategy_type + strategy_id (varchar mit CHECK) - **Migrationsempfehlung:** ADD COLUMN strategy_group + Index; kein UPDATE-Backfill; COALESCE im Reader - **Phasenplan:** T-SPLIT-1 (Schema) → T-SPLIT-3 (GUI sofort sichtbar) → T-SPLIT-2 (Bot-Emit + Restart) → T-SPLIT-4 (G3) → T-SPLIT-5 / T-SPLIT-6 (separat) - **10 offene Fragen TQ-1..TQ-10** mit Default-Empfehlungen für User-Entscheidung - **Kein Bedarf für T-SPLIT-5/6 in MVP** — T-SPLIT-1..4 reicht für funktionsfähige Sichtbarkeit + Sauberkeit **Empfohlener nächster Schritt:** User entscheidet Q1-Q10, dann GO für **T-SPLIT-1** (Schema-Migration mit DB-Backup-Pflicht). T-SPLIT-2 (Bot-Restart-Phase) erst nach T-SPLIT-1 + T-SPLIT-3 grün. --- *Generated 2026-05-09 18:00 UTC — T-SPLIT-PREFLIGHT read-only. Bot PID 4246 stable, runtime_config.json absent, .env mtime unverändert, kein Mainnet, kein Push, kein Worker.*