Files
Finlytic/PROBLEMS.md
T

222 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 123147)
- **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 8286)
- **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 **815%** 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 13 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 |