# ALERTS · Nightly 2026-05-18 Quelle: read-only Forensik aus bot_stdout.log (125 MB), live_portfolio.json, dca_log.json (state-volume snapshot 02:44 lokal) und state-backups/20260518T001433Z_pre_shib_fix. Keine Code-Änderung, keine State-Mutation, keine Runtime-Berührung. --- ## P0-A · SHIB Phantom-Position-Sell-Loop · Root-Cause identifiziert **Severity:** P0 **Status:** Symptom durch SHIB-STATE-DRIFT-RECONCILE-1 manuell beseitigt (commit `faa86b0`, state-backup `20260518T001433Z_pre_shib_fix`). **Code-seitige Ursache:** noch offen — kein automatischer Selbstheilung-Pfad. ### Befund Auf 2026-05-17 hat die Bot-Runtime für `SHIB/USDT` **29 SL-Trigger-Logs** plus **29 fehlgeschlagene TESTNET SELL-Lines** erzeugt — und **0 `Position geschlossen`-Lines**, **0 closed_trade-Einträge**. ``` SL-trigger by symbol (2026-05-17): 29 SHIB, 2 XAUT, 1 XLM/TRUMP/TAO/ORCA/HUMA/ENA/ATOM TESTNET SELL by symbol (2026-05-17): 29 SHIB (alle ERROR), 2 XAUT, 1 … "Position geschlossen" (2026-05-17): 0 SHIB, 2 XAUT, 1 … ``` Für **alle anderen** Symbole stimmt das 1:1:1-Verhältnis Trigger → SELL → Close. Nur SHIB ist im Loop hängen geblieben. ### Belege - erste fehlgeschlagene SHIB-SELL: ``` 2026-05-17 01:06:32 | execution.live_trade | ERROR | TESTNET SELL SHIB/USDT fehlgeschlagen: reason=insufficient_funds category=permanent err=binance Account has insufficient balance … ``` - über **22 Stunden** weiter (~37 Fehlversuche, plus 1 zwischenzeitlich erfolgreicher DCA-Kauf um 01:27:09). - Code-Pfad: `trading/execution/live_trade.py:714-721` — bei `not order_out.ok` wird nur geloggt + `_track_sell_failure(symbol)` inkrementiert + `return None`. Die Position bleibt im in-memory-State und wird nicht aus `live_portfolio.json` entfernt. Nächster Scan-Zyklus fired denselben SL-Trigger erneut. ### Risiko - ohne manuellen Eingriff loopt jeder Phantom-Position-Sell-Versuch endlos. - Im Mainnet würde ein **echter** `insufficient_funds`-Befund auf einem hängenden Position-State zur Trader-API-Spam-Welle führen (Rate-Limit-Risiko) und das Operator-Dashboard mit falschen offenen Positionen vergiften. - DCA-Budget-Cap wirkt nicht heilend — `DCA Veto Langfrist-Downtrend` und `Budget-Cap erreicht`-Warnings haben keinen Selbstheilung-Hook in den Position-Lifecycle. ### Empfehlung (kein Code, kein Touch — nur Plan) 1. **STATE-EXCHANGE-RECONCILE-LOOP** Roadmap-Entry ist bereits geplant (commit `faa86b0`). Diesen Plan dahingehend schärfen: - bei `category=permanent reason=insufficient_funds` SELL-Fail → Position als `STUCK_NO_EXCHANGE_BALANCE` markieren (NICHT silent entfernen) - Telegram-Alert + GUI-Banner - Operator-GO erforderlich für State-Cleanup - Bot pausiert SL-Trigger-Emission für diese Symbol, bis Operator entschieden hat → kein Loop 2. **Pre-Trade Balance-Sanity**: vor jedem SL-Trigger einmalig `fetch_balance(symbol)` aufrufen und wenn `free` < `position.quantity`, markieren statt selling — verhindert Loop-Start. 3. **POSITION-SOURCE-OF-TRUTH P1** (siehe Sektion „POSITION-SOT" unten) ist Voraussetzung für saubere Reconcile-Logik. **Operator-GO erforderlich** für jede Code-Änderung an `trading/execution/live_trade.py` oder `trading/execution/paper_trade.py`. --- ## P0-B · `_track_sell_failure` ohne sichtbare Heilung **Severity:** P0 **Verbunden mit:** P0-A ### Befund `trading/execution/live_trade.py:720` ruft `_track_sell_failure(symbol)`. Im Log ist während der 22h-Schleife **keine** zugehörige Alert/State-Heilung zu sehen (kein `STUCK_POSITION`-Hinweis im bot_stdout.log am 17.05.). ### Risiko Der „stuck-position alert" aus B-OUTAGE-RESILIENCE-1 scheint entweder - nie zu feuern (Threshold zu hoch), oder - silently zu loggen (nicht prominent ins Operator-Telegram). ### Empfehlung Read-only-Folgearbeit für eine spätere Phase: - Threshold + Notification-Pfad für `_track_sell_failure` prüfen - Sicherstellen dass nach N=3 permanent-failures ein Telegram-Alert und ein DB-`bot_statuses`-Eintrag entsteht - Code-Lokation: noch zu suchen (Suchpattern `_track_sell_failure`). Nicht in dieser Session geöffnet, weil read-only-Scope. --- ## P1-A · DCA-State-Persistence-Gap (Plan-Prämisse veraltet) **Severity:** P1 **Bezug:** `docs/PLAN_DCA_STATE_RECONCILE.md` ### Befund `docs/PLAN_DCA_STATE_RECONCILE.md` listet als Forensik-Targets: > Aktuell offene Positionen mit DCA-Historie: ENA · XLM · SHIB **Stand 2026-05-18 02:44 UTC** (live state): - **0** dieser 3 Symbole ist noch offen. - Einzige aktuell offene Position: `TON/USDT` (qty 110.64, entry 1.908). | Symbol | Plan-Status | Tatsächlicher Status (live state) | |---|---|---| | ENA/USDT | offen + 2 DCA | 2026-05-17 23:18:05 closed (`fixed_stop_loss`, PnL -13.29) | | XLM/USDT | offen + 1 DCA | 2026-05-17 23:42:11 closed (`legacy_stop_loss`, PnL -8.67) — die DCA-Position vom 16.05. wurde bereits 2026-05-16 10:18 geschlossen, danach Reopen am 16.05. 13:40 OHNE DCA | | SHIB/USDT | offen + 1 DCA | 2026-05-17 23:18-23:51 stuck (loop), 2026-05-18 00:14 manuell aus State entfernt | ### Risiko Plan-Acceptance-Criteria zielen auf nicht mehr existierende Positionen. Forensik-Wert reduziert sich auf historische Verifikation aus dem Log. Ohne Plan-Update droht die Phase mit veraltetem Scope zu starten. ### Empfehlung - Plan in `docs/PLAN_DCA_STATE_RECONCILE.md` aktualisieren: - Sektion 1 „Ausgangslage" auf Stand 2026-05-18 02:44 UTC bringen - Forensik-Scope auf historische Rekonstruktion aus bot_stdout.log reduzieren - Neue offene Position TON/USDT (1 Tranche, keine DCA) als laufendes Beobachtungs-Target führen - Folgeplan `DCA-STATE-SAVE-RACE-FIX` bleibt sinnvoll, **wenn** STATE-EXCHANGE-RECONCILE-LOOP zuerst erledigt ist. --- ## P1-B · dca_log.json — orphaned entries **Severity:** P1 **Bezug:** [[position_source_of_truth_20260518]] ### Befund `dca_log.json` (live state, 02:44 UTC) enthält **26 Einträge**. Davon hat **1** ein passendes offenes Position-Objekt (`TON/USDT`). **25 Einträge** sind Orphans. Sample-Belege (vollständig in `reports/nightly/2026-05-18/DCA_STATE_FORENSICS.md`): | Symbol | Letzter Close (live state) | DCA-Eintrag noch da? | |---|---|---| | KITE/USDT | 2026-05-16 10:09 manual_close | ja (2 Tranches: tranche_1, Pyramid) | | SUI/USDT | 2026-05-16 15:01 manual_close | ja (1 Tranche tranche_1_t3fwd) | | KSM/USDT | nie in closed_trades | ja (1 Tranche tranche_1) | | CBBTC/USDT| nie in closed_trades | ja (1 Tranche tranche_1_t3fwd, 2026-04-22) | | BABY/USDT | nie in closed_trades | NEIN — wurde zwischen 00:14 und 02:24 entfernt | Im Log greift `🗑️ DCA Log bereinigt` nur bei **erfolgreichem** `execute_sell` aus `paper_trade`. CommandBus-`manual_close`, SHIB-State-Drift-Cleanup und ghost-Einträge aus dem alten Bot-Zeitraum hinterlassen Orphans. ### Risiko - `dca_log.json` ist als „Source of Truth für DCA-Tranchen" praktisch unbrauchbar (Confidence: low). - Future DCA-Decisions die `dca_log` lesen könnten falsche Budget-Caps/Pyramid-Counts erhalten, wenn ein Symbol re-opened wird, das schon einen Orphan-Eintrag hat (potenzielles Phantom-Pyramid-Cap). - Verstößt gegen Bullet „DCA-Events im Log nicht in dca_log.json / State sichtbar" aus dem Nightly-Prompt (Alert-Trigger). ### Empfehlung - Code-seitiger Folgepunkt für späteren Plan: DCA-Log-Cleanup-Hook in **alle** Close-Pfade einhängen - `paper_trade.execute_sell` (existiert) - `command_worker._handle_close_position` (fehlt) - State-Drift-Reconcile-Pfad (fehlt) - Zusätzliche Periodic-Reaper: tägliche Aufgabe entfernt dca_log-Einträge ohne offene Position **und** ohne Tranche-Activity in N Tagen - Eigenen Roadmap-Eintrag `DCA-LOG-ORPHAN-REAPER` (P2) anlegen --- ## P1-C · `entry_price` Semantik: in closed_trades = Average, nicht Initial **Severity:** P1 **Bezug:** Backlog `ENTRY-PRICE-SEMANTIC-CLARIFY P1` ### Befund Verifikation ENA-Trade vom 2026-05-17 23:18:05 ist eindeutiger Test-Case: | Quelle | Wert | |---|---| | TESTNET BUY (2026-05-16 05:50:30) | `@ 0.109600` (initial entry) | | DCA #1 (07:14:49) | `Neuer Avg: 0.1083` | | DCA #2 (10:08:00) | `Neuer Avg: 0.1077` | | live_portfolio.closed_trades.entry_price | `0.107674` | | live_portfolio.closed_trades.exit_price | `0.1052` | | live_portfolio.closed_trades.pnl | `-13.29` | `entry_price` im closed_trade ist nicht der initial-Entry-Wert 0.1096, sondern der DCA-gewichtete Average 0.10767 (rundet auf 0.1077). **Confidence: high.** ### Risiko - Operator-Dashboard zeigt im PositionDetail / TradeLog-Liste „Entry: 0.1077" — das ist semantisch **Average-nach-DCA**, nicht Initial-Entry. - PnL-pct-Berechnungen, die `(exit - entry) / entry` rechnen, kommen auf -2.3% — korrekt aus „avg" Sicht, aber Operator denkt vielleicht „SL hat ein paar Pips unter Initial-Entry getroffen". - Initial-Entry-Wert ist in keiner Tabelle persistiert. Nur im `bot_stdout.log` rekonstruierbar (linke Spalte oben). ### Empfehlung - Roadmap-Eintrag `ENTRY-PRICE-SEMANTIC-CLARIFY P1` ist gepinnt → keine neue Roadmap-Aktion nötig. - Quick-Win-Vorschlag für die GUI-Entwurfsphase (P2 Open-Positions-View): zwei Felder darstellen — `Initial Entry` (aus erstem tranche im dca_log oder erstem trade_log mit gleichem position_id) und `Average Entry` (aktuelles `entry_price` / dca_log `avg_price`). - Code-Backend für Initial-Entry-Persistenz: separater Plan `INITIAL-ENTRY-PERSIST P2` (neuer Eintrag empfohlen). --- ## P1-D · Event-Counter-Consistency-Check Re-Interpretation **Severity:** P1 **Bezug:** Operator-Bullet „6 SL-trigger-Logs vs. 2 closed_trades aus Session 2026-05-17" ### Befund Aus dem vollständigen 2026-05-17-Window: | Symbol | SL-Trigger | TESTNET SELL ok | TESTNET SELL ERROR | Position geschlossen | |---|---|---|---|---| | SHIB/USDT | 29 | 0 | 29 (insufficient_funds) | 0 | | XAUT/USDT | 2 | 2 | 0 | 2 | | ATOM/USDT | 1 | 1 | 0 | 1 | | ORCA/USDT | 1 | 1 | 0 | 1 | | HUMA/USDT | 1 | 1 | 0 | 1 | | TAO/USDT | 1 | 1 | 0 | 1 | | ENA/USDT | 1 | 1 | 0 | 1 | | TRUMP/USDT| 1 | 1 | 0 | 1 | | XLM/USDT | 1 | 1 | 0 | 1 | Counter-Inkonsistenz ist **ausschließlich SHIB**, Ursache identisch zu P0-A. Alle anderen Symbole: 1:1:1 sauber. Die Operator-Bullet-Annahme „6 SL vs. 2 closed_trades" deckt vermutlich ein **kleineres Zeitfenster** ab (z. B. nur die 23:18-23:42-Welle) oder zählt SHIB nicht mit. Die Substanz der Beobachtung ist trotzdem korrekt: **Counter laufen auseinander, wenn ein SELL permanent-fails**. ### Risiko Nichts darüber hinaus, was P0-A nicht schon abdeckt. ### Empfehlung - Operator-Bullet im Backlog beim nächsten Update mit präziser Zahl ersetzen: „SHIB/USDT: 29 SL-Trigger, 29 fehlgeschlagene SELLs, 0 Closes — 22h Loop am 2026-05-17." - Backlog-Eintrag ist effektiv dieselbe Phase wie `STATE-EXCHANGE-RECONCILE-LOOP` + `STUCK-POSITION-ALERT-PATH`. --- ## P2-A · `live_portfolio.json` Source-of-Truth Klärung nötig **Severity:** P2 **Bezug:** Operator-Bullet „POSITION-SOURCE-OF-TRUTH P1" Siehe separates Dokument: `reports/nightly/2026-05-18/POSITION_SOURCE_OF_TRUTH.md`. --- ## Was NICHT als Alert markiert wurde - Pyramid-Käufe in KITE und BABY waren **strategisch korrekt** (Pyramid: Kurs > Entry-Trigger), nicht DCA-Rescue. Reason-String differenziert bereits (`Pyramid:` vs. `DCA-Rettung`). Keine Aktion. - TON/USDT (offen) hat 1 Tranche, kein DCA, alles konsistent. - T2/T3-Aktivität: keine Spuren im Log (T2/T3 sind disabled, Bullet „T2/T3-Aktivität in DB trotz Deaktivierung" → keine Funde). - Risk-Guard / C1/C2/D Touch: keine Code-Diffs in dieser Session (read-only). --- ## Operator-GO-Anforderungen | Empfehlung | GO nötig? | Wer betroffen | |---|---|---| | Plan-Update `PLAN_DCA_STATE_RECONCILE.md` | nein (Doku) | — | | Neuer Plan `PLAN_STATE_EXCHANGE_RECONCILE.md` | nein (Doku) | — | | Neuer Backlog-Eintrag `DCA-LOG-ORPHAN-REAPER P2` | nein (Doku) | — | | Code-Fix `live_trade.py:714-721` (Phantom-Loop) | **JA** | Bot, Mainnet-Pfad | | Periodische dca_log-Cleanup-Task | **JA** | Bot | | Initial-Entry-Persistenz im trade_log | **JA** | Bot + DB | | GUI-Banner „STUCK POSITION" | nein (Doku) | GUI-Entwurf P2 |