# Finlytic Problemanalyse & Schwachstellenbericht (PROBLEMS.md) Dieses Dokument analysiert detailliert alle identifizierten Fehler, logischen Inkonsistenzen, mathematischen/finanziellen Ungenauigkeitsquellen, fehlenden Funktionen und Skalierungsrisiken im gesamten Finlytic-Codebase. --- ## Inhaltsverzeichnis 1. [Kritische Bugs & Logikfehler](#1-kritische-bugs--logikfehler) 2. [Ursachen für Ungenauigkeiten (Inaccuracies & Drift)](#2-ursachen-für-ungenauigkeiten-inaccuracies--drift) 3. [Fehlende Funktionen & Architekturlücken](#3-fehlende-funktionen--architekturlücken) 4. [Skalierungs-, Performance- & Resilienz-Risiken](#4-skalierungs--performance---resilienz-risiken) 5. [Konkreter Maßnahmen- & Optimierungs-Fahrplan](#5-konkreter-maßnahmen---optimierungs-fahrplan) --- ## 1. Kritische Bugs & Logikfehler ### 1.1 Alpaca Bracket-Order Stop-Loss Update Fehler - **Ort**: `FinlyticBot/Services/Alpaca/AlpacaPaperTradingService.cs` (Zeilen 123–147) - **Problem**: Beim Platzieren einer Bracket-Order (`PostOrderAsync`) gibt Alpaca die Order-ID der **übergeordneten Market-Order** zurück. Sobald diese ausgeführt wird, ist diese Order abgeschlossen (`filled`). In `UpdateStopLossAsync` wird versucht, `client.PatchOrderAsync(new ChangeOrderRequest(orderGuid) { StopPrice = ... })` direkt mit der übergeordneten Market-Order-ID aufzurufen. - **Auswirkung**: Alpaca lehnt das Update mit `422 Unprocessable Entity` oder `404 Not Found` ab, da nicht die Parent-Order, sondern die untergeordnete Stop-Loss-Leg-Order gepatcht werden muss. - **Lösung**: Nach der Ausführung muss die Order über `client.GetOrderAsync()` abgefragt werden, um die `legs` (Child-Orders) zu inspizieren und die ID der Stop-Loss-Order in `BotPositionEntity` zu speichern. --- ### 1.2 Bot-Positionsüberwachung fragt nicht existierende 1m-Kerzen ab - **Ort**: `FinlyticBot/Services/Monitoring/BotTradeLifecycleBackgroundService.cs` (Zeile 82–86) - **Problem**: Der Lifecycle-Service pollt `ta_GetCandles` mit `Timeframe = "1m"`. `FinlyticTechnicals` befüllt seine Ringpuffer jedoch primär via Yahoo Finance mit den Timeframes `15m`, `1h` und `1d`. Die `1m`-Kerzen werden ausschließlich live generiert, wenn `TradeRepublicIngestionService` für genau dieses Asset Ticks streamt. - **Auswirkung**: Für Assets, die nicht aktiv über Trade Republic gestreamt werden, gibt `ta_GetCandles` eine leere Liste zurück (`candles.Count == 0`). Der Bot führt `continue` aus und aktualisiert weder den aktuellen Kurs (`CurrentPrice`), noch prüft er Stop-Loss- oder Take-Profit-Bedingungen. - **Lösung**: Fallback auf den kleinsten verfügbaren Timeframe (`15m`) oder direkte Abfrage des letzten Live-Kurses (`tr_GetLivePrice`). --- ### 1.3 Inkonsistente Datenbankbenennung für FinlyticSentiment - **Ort**: `compose.yaml` (Zeile 138) vs. Dokumentation & Konventionen - **Problem**: In `compose.yaml` heißt die Datenbank `finlytic_sentimental`, während die Namenskonvention aller anderen Services `finlytic_{service}` lautet (also `finlytic_sentiment`). - **Auswirkung**: Bei automatisierten Backups, Init-Skripten oder manuellen SQL-Inspektionen führt dieser Tippfehler zu Verwirrung oder fehlgeschlagenen Migrations-Skripten. - **Lösung**: Vereinheitlichung auf `finlytic_sentiment`. --- ### 1.4 Unbenutzte Gebührenvariable im Synthetischen Ledger - **Ort**: `FinlyticBot/Services/Ledger/SyntheticPaperBroker.cs` (Zeile 134, 149) - **Problem**: In `GetSummaryAsync` wird `decimal totalFees = positions.Sum(p => p.TotalFeesEur);` berechnet, aber in der Equity-Formel nicht verwendet: `decimal currentEquity = baseCapital + totalRealized + unrealizedPnl;` *(Hinweis: `totalRealized` hat die Gebühren bereits bei Schließung abgezogen; die Variable `totalFees` ist toter Code).* - **Lösung**: Bereinigung oder explizite Dokumentation der Netto-PnL-Logik. --- ## 2. Ursachen für Ungenauigkeiten (Inaccuracies & Drift) ### 2.1 Fehlende Währungskonvertierung (USD vs. EUR bei Alpaca) - **Ort**: `FinlyticBot/Services/Execution/BotOrderExecutor.cs` & `FinlyticBot/Services/Alpaca/AlpacaPaperTradingService.cs` - **Problem**: - Finlytics Kontoführung, synthetischer Ledger und Risikoberechnungen (`SyntheticBaseCapitalEur`, Sizing-Formel) rechnen strikt in **EUR (€)**. - Alpaca US-Equities (z.B. AAPL, NVDA) werden in **USD ($)** abgerechnet und bepreist. - `BotOrderExecutor` übergibt den EUR-Preis 1:1 an Alpaca bzw. nimmt für das Sizing an, dass $1 = 1 €$. - **Ungenauigkeit**: Je nach EUR/USD-Wechselkurs (z.B. 1,08) weicht das tatsächliche Risiko um **8–15%** von der 1%-Risikoregel ab. - **Lösung**: Integration eines FX-Umrechnungskurses (z.B. über EZB-Feed oder Yahoo EURUSD=X) in die Sizing- und Positionsbewertungslogik. --- ### 2.2 Warmup-Verzerrung bei EMA 200 & langfristigen Indikatoren `[BEHOBEN]` - **Ort**: `FinlyticTechnicals/Indicators/TechnicalIndicatorsEngine.cs`, `CoreStrategies.cs` & `TechnicalScoringEngineV2.cs` - **Problem**: Wenn für ein neu hinzugefügtes Asset weniger als 200 historische Kerzen vorlagen, wurde der EMA 200 aus den verfügbaren Kerzen berechnet (Fallback auf SMA über z.B. 50 Kerzen). - **Lösung / Status**: **Behoben**: `CalculateEma` gibt bei `candles.Count < period` strikt `0m` zurück. `TrendPullbackFvgStrategy` und `MovingAverageCrossoverStrategy` prüfen strikt $\ge 205$ Kerzen, und `TechnicalScoringEngineV2` vergibt Confluence-Punkte nur bei `EMA > 0m`. --- ### 2.3 Intrabar-Pfad-Ungewissheit im Backtesting `[BEHOBEN]` - **Ort**: `FinlyticSimulation/Engine/VirtualBacktestBroker.cs` - **Problem**: Eine Kerze liefert nur $O, H, L, C$. Wenn innerhalb derselben Kerze sowohl das Take-Profit-Level ($H$) als auch das Stop-Loss-Level ($L$) berührt wurden, konnte der Backtester nicht feststellen, welches Extremum zuerst eintrat. - **Lösung / Status**: **Behoben**: Konservatives Worst-Case-Prinzip implementiert. Stop-Loss und Knock-Out-Checks werden strikt vor Take-Profit ausgeführt. Wird TP1 in einer Kerze ausgelöst und der Stop auf Break-Even gezogen, wird sofort geprüft, ob das Bar-Tief auch das Break-Even-Level schneidet, um die Restposition ggf. direkt als Break-Even auszustoppen. --- ### 2.4 Feste Slippage `[BEHOBEN / ENTFERNT]` - **Ort**: `FinlyticSimulation/Engine/VirtualBacktestBroker.cs` & `SimulationSettingKeys.cs` - **Problem**: Bisher wurde neben der festen Ordergebühr zusätzlich eine prozentuale Slippage (0.05%) auf Kursdaten angewendet. - **Lösung / Status**: **Behoben**: Künstlicher Slippage-Aufschlag/-Abschlag vollständig aus der Kursausführung entfernt; Transaktionskosten werden transparent und sauber über die Ordergebühren (`_orderFeeEur = 1.00 €`) abgebildet. `DefaultSlippagePercent` wurde auf `0.0m` gesetzt. --- ### 2.5 Trade Republic WebSocket-Inaktivitäts-Timeout - **Ort**: `FinlyticCore/Services/TradeRepublic/TradeRepublicService.cs` (`_inactivityTimer = 461 Sekunden`) - **Problem**: Wenn 7,6 Minuten lang keine Anfrage an Trade Republic gestellt wird, schließt der Timer die WebSocket-Verbindung. Bei der nächsten Anfrage muss die Verbindung neu aufgebaut werden. - **Ungenauigkeit**: Der Neuaufbau dauert 1–3 Sekunden. In dieser Zeit schlagen Live-Kurs-Abfragen fehl oder liefern veraltete Cache-Preise. - **Lösung**: Automatischer Ping/Keepalive statt Schließung oder resilienter Reconnect mit Retry. --- ### 2.6 Typkonvertierungen (`double` vs. `decimal`) - **Ort**: Mehrere Services (FinBERT DTOs nutzen `double`, Engine/Technicals nutzen `decimal`) - **Problem**: In `CompositeOpportunityScorerV2` wird `(decimal)sentiment.CurrentSummary.CompoundScore` gecastet. Fließkommazahlen (`double`) können binäre Rundungsfehler aufweisen (z.B. `0.15000000000000002`). - **Lösung**: Rundung auf 4 Nachkommastellen vor dem Casten (`Math.Round((decimal)score, 4)`). --- ## 3. Fehlende Funktionen & Architekturlücken ### 3.1 Fehlende Short-Derivate-Alternativen bei Trade Republic - **Ort**: `FinlyticEngine/Services/Derivatives/KnockOutDerivativeResolver.cs` - **Lücke**: Wenn `FinlyticTechnicals` ein starkes Short-Signal (Verkauf) generiert, sucht der Resolver ausschließlich nach `knockOutProduct` mit `OptionType.Short` (Put Knock-Outs). Gibt es für das Asset keine KO-Puts bei Trade Republic, scheitert die Derivate-Zuweisung komplett. - **Erweiterung**: Automatischer Fallback auf klassische Put-Optionsscheine (`vanillaWarrant`) oder Faktor-Short-Zertifikate. --- ### 3.2 Keine Portfolio-Korrelations- & Branchenrisiko-Prüfung - **Ort**: `FinlyticBot/Services/Execution/BotOrderExecutor.cs` - **Lücke**: Der Bot prüft lediglich, ob `activeCount < MaxConcurrentPositions` (5) ist. Er prüft nicht, ob alle 5 Positionen aus demselben Sektor stammen (z.B. 5x Halbleiter/Tech). - **Erweiterung**: Sektoren-Exposure-Limit: Maximal 2 Positionen pro Sektor oder maximal 40% Gesamtallokation in einer Branche. --- ### 3.3 Fehlende Multi-User-Isolation im Bot - **Ort**: `FinlyticBot/Database/Entities/BotPositionEntity.cs` - **Lücke**: `EngineTradeEntity` in `FinlyticEngine` besitzt bereits ein `UserId`-Feld für Multi-Tenancy. `BotPositionEntity` im `FinlyticBot` besitzt jedoch **kein `UserId`-Feld** – alle Bot-Trades laufen in einem globalen Pool. - **Erweiterung**: Erweiterung von `BotPositionEntity` um `UserId` und Filterung im `BotController` nach dem authentifizierten Benutzer. --- ### 3.4 Fehlender nativer Trailing-Stop bei Alpaca - **Ort**: `FinlyticBot/Services/Alpaca/AlpacaPaperTradingService.cs` - **Lücke**: Alpaca unterstützt native Trailing-Stop-Orders (`trailing_stop`). Der Service nutzt bisher nur feste Bracket-Orders und versucht, den Stop-Loss diskret im 15s-Polling-Intervall nachzuziehen. - **Erweiterung**: Nutzung der nativen Alpaca `TrailingStopOrder`-API für exaktes Tick-basiertes Nachziehen ohne Latenzrisiko. --- ### 3.5 Fehlende Historienbereinigung (Data Retention Cleanup Cron) - **Ort**: `FinlyticNews`, `FinlyticSentiment`, `FinlyticEngine` - **Lücke**: Obwohl `SettingKeys.ArticleRetentionDays` (90 Tage) existiert, läuft kein automatischer Hintergrund-Cleanup-Job, der abgelaufene Artikel, Snapshots oder Logs physisch aus der PostgreSQL-Datenbank löscht. - **Erweiterung**: Einrichten eines täglichen Wartungs-Background-Services (`DataRetentionCleanupWorker`). --- ## 4. Skalierungs-, Performance- & Resilienz-Risiken ### 4.1 Unbegrenztes Speicherwachstum bei In-Memory-Ringpuffern - **Ort**: `FinlyticTechnicals/Services/MultiTimeframeCandleAggregator.cs` - **Risiko**: `_buffers` hält für jedes jemals abgefragte Asset ein `ConcurrentDictionary` mit je 500 Kerzen über 5 Timeframes. Werden über den Scanner 10.000 Assets abgefragt, belegt dies mehrere Gigabyte RAM im Container. - **Lösung**: Einführung einer LRU-Cache-Bereinigung (z.B. `MemoryCache` mit Ablaufzeit für Assets außerhalb der Watchlist). --- ### 4.2 Playwright-Browser-Instanzen & Zombie-Prozesse - **Ort**: `FinlyticNews/Services/PlaywrightScraperService.cs` & `FinlyticFundamentals` - **Risiko**: Playwright startet Chromium-Headless-Instanzen. Bei Netzwerk-Timeouts oder abrupten Thread-Abbrüchen können verwaiste `chrome`-Prozesse im Docker-Container verbleiben und Speicher/CPU leersaugen. - **Lösung**: Striktes `using`-Ressourcenmanagement mit `BrowserContext.CloseAsync()` und Docker-Container-Speicherlimits (`mem_limit` in `compose.yaml`). --- ### 4.3 Rate-Limiting & IP-Blocking bei Yahoo Finance - **Ort**: `FinlyticCore/Clients/YahooFinanceClient.cs` & `YahooFinanceScraper.cs` - **Risiko**: Yahoo Finance besitzt unangekündigte Rate-Limits. Werden 100 Assets parallel gescannt, antwortet Yahoo mit `HTTP 429 Too Many Requests`. - **Lösung**: Zentraler Request-Throttler mit Polly-Retry und Exponential-Backoff im `YahooFinanceClient`. --- ### 4.4 Single Point of Failure (MQTT-Broker & OmniDB) - **Risiko**: Alle Microservices sind über einen einzelnen MQTT-Broker verbunden. Fällt dieser aus, bricht die gesamte Inter-Service-Kommunikation ab. - **Lösung**: Polly-basierte Reconnect-Pipelines sind in `ManagedMqttClient` vorhanden, sollten jedoch mit Offline-Queuing ergänzt werden. --- ## 5. Konkreter Maßnahmen- & Optimierungs-Fahrplan | Priorität | Bereich | Maßnahme | Aufwand | | :---: | :--- | :--- | :---: | | 🔴 **P1** | `FinlyticBot` | **Alpaca Bracket-Order Leg-ID Fix**: Stop-Loss-Leg nach Orderplatzierung ermitteln und speichern, um `UpdateStopLossAsync` funktionsfähig zu machen. | Gering | | 🔴 **P1** | `FinlyticBot` | **1m-Kerzen-Polling beheben**: Fallback auf `15m` oder `tr_GetLivePrice` im `BotTradeLifecycleBackgroundService`. | Gering | | 🟡 **P2** | `FinlyticBot` | **USD/EUR Währungskonvertierung**: Integration eines dynamischen Wechselkurses für US-Positionen. | Mittel | | 🟡 **P2** | `FinlyticEngine` | **Derivate-Fallback erweitern**: Optionsscheine/Faktor-Zertifikate als Fallback bei fehlenden KO-Puts. | Mittel | | 🟡 **P2** | `FinlyticTechnicals`| **EMA 200 Warmup-Guard**: Keine Signalfreigabe bei unvollständiger Kerzenhistorie ($<200$). | Gering | | 🟢 **P3** | `FinlyticBot` | **Multi-User Isolation**: `UserId` zu `BotPositionEntity` hinzufügen. | Mittel | | 🟢 **P3** | `FinlyticCore` | **Data Retention Background-Worker**: Automatisches Löschen alter News/Logs nach 90 Tagen. | Mittel | | 🟢 **P3** | `FinlyticTechnicals`| **LRU-Cache für Ringpuffer**: Speicherdeckelung bei großen Asset-Zahlen. | Mittel |