Files
PolyTraderSharp/docs/umsetzungsplaene/UMSETZUNGSPLAN-CopyTrading-Verbesserungen.md
T
RichardandClaude Opus 4.8 c5f0b1d188 Docs: Konzepte/Plaene in docs/ mit Typ-Unterordnern buendeln
Root aufgeraeumt: alle Konzept-/Plan-/Fach-Dokumente nach docs/ verschoben,
organisiert nach Typ (wie fuer ein separates Docs-Repo vorgeschlagen, aber bewusst
in diesem Repo, damit Plan->umsetzende-Commits nachvollziehbar bleiben):
- docs/konzepte/       (KONZEPT-*)
- docs/umsetzungsplaene/ (UMSETZUNGSPLAN-*)
- docs/ideen/          (fruehe Ideen, Platzhalter)
- docs/pruefplaene/    (PRUEFPLAN-*)
- docs/steuer/         (Steuer-/Buchhaltungs-Doks, z.B. US-CPA-Fragebogen)
- docs/README.md       (Index/Konventionen)

Getrackte Plaene als Rename verschoben (History erhalten); zuvor untracked Konzept-/
Plan-Dateien jetzt versioniert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 10:15:08 +02:00

315 lines
15 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.
# Umsetzungsplan: Copytrading-Modul — Rentabilitäts-Verbesserungen
> Stand: 2026-07-06
> Ziel: Bekannte Verlustquellen im Copytrading-Modul beseitigen und die
> Rentabilität durch datengetriebene Trader-Auswahl, echte Fill-Daten und
> besseres SELL-Handling steigern.
> Reihenfolge: **Dieser Plan zuerst.** Phase 1 (Marktdaten-Fundament) ist
> Voraussetzung für die Strategiemodule MarketMaking und BundleArbitrage.
---
## 0. Kontext & Hintergrund (für die Umsetzung ohne Vorwissen)
Das Copytrading-Modul (`src/PolyTrader.Modules.CopyTrading/`) kopiert Trades von
Master-Tradern auf Polymarket. Signalkette:
1. `AlchemyWebsocketService` erkennt On-Chain-Events der Master-Wallets (WSS).
2. `TraderMonitorService.TriggerFastBlockchainPoll()` parst die Transaktion direkt
(Fast Track, ~34 s hinter dem Master) oder fällt auf Data-API-Polling zurück.
3. Signale (`CopySignal`) laufen über einen Channel in die `CopyTradingEngine`.
4. Die Engine prüft Risiko-Limits (`CopyTradingAccountSettings`: PerMarketLimit,
PerMasterLimit, Zeitfenster-Limits, MaxBuyPrice) und platziert CLOB-Orders
über `PolymarketClobClient` (Core).
**Historischer Kontext (wichtig!):** Eine Verlustanalyse im April 2026
(`agentspace/prompts/AnalyzingOvernightTradingLosses.md`) hat als Hauptursache
für Overnight-Verluste identifiziert, dass der Bot SELLs der Master mit
Market-Orders ins leergeräumte Orderbuch kopiert und so zur „Exit-Liquidity"
wird (Beispiel: Entry 0.51, Master-Exit 0.99, unser Exit 0.49). Der damalige
Fix (GTD-Limit-Sells) ist **im aktuellen Modul-Code nicht mehr vorhanden**
vermutlich bei der Modularisierung verloren gegangen.
**Seit März 2026 erhebt Polymarket Taker-Fees** (Sports ~0,75 %, Politik/Finanzen
~1,0 %, Krypto ~1,8 %, am 50-¢-Preis am höchsten, Richtung 1 ¢/99 ¢ abnehmend;
Maker zahlen nichts und erhalten Rebates). Der Bot ist heute fast immer Taker.
Quelle: https://docs.polymarket.com/trading/fees — **bei Umsetzung aktuellen
Stand verifizieren.**
### Leitplanken (gelten für alle Phasen)
1. `.agents/rules/clob.md` beachten: Änderungen an der CLOB-Integration sind
hochkritisch. Vor jeder Änderung Backup/Commit der alten Version, jede
Änderung mehrfach prüfen.
2. Jede Phase lässt die App baubar und lauffähig zurück (Debug-Build grün).
3. Entscheidungslogik als testbare, pure Funktionen extrahieren und in
`PolyTrader.Tests` (xUnit, existiert bereits) abdecken.
4. Kein Livegang einer Phase ohne mehrtägige Beobachtung auf dem Server.
---
## Phase 0 — Sofortmaßnahmen: Blutung stoppen
### 0.1 🔴 SELL-Exit-Liquidity-Regression beheben (höchste Priorität)
**Befund:** In `CopyTradingEngine.cs` (Live-SELL-Pfad, aktuell ~Zeile 694735)
werden SELLs als `"MARKET"` mit Fallback-Limit `0.01m` gesendet:
```csharp
decimal sellLimit = 0.01m; // Market Order Fallback Limit
...
var result = await _clob.PlaceOrderAsync(account, signal.TokenId, signal.Side,
expectedUsdc, sellLimit, "MARKET", _state.DebugOrderPayloadLog, isNegRisk);
```
Das ist exakt das Verhalten, das die April-Verluste verursacht hat.
**Ziel-Design: Eskalationsleiter statt Market-Order**
1. Referenzpreis = `signal.Price` (Exit-Preis des Masters).
2. Erste Order: GTD-Limit.
- HF-Trader (`trader.Category == "HF"`): `signal.Price - 0.005m`.
- Sonst: `signal.Price * (1 - settings.MaxPriceDifference / 100m)`.
3. Neuer Setting-Wert `SellFloorPct` in `CopyTradingAccountSettings`
(Default z. B. 15 %): absolute Untergrenze = `signal.Price * (1 - SellFloorPct/100)`.
4. Hintergrund-Loop (Erweiterung von `CleanupStaleOpenOrdersAsync` in
`TraderMonitorService` oder eigener Loop): Order nach T Sekunden ohne Fill
(HF: ~20 s, sonst: ~120 s) canceln und eine Stufe tiefer neu platzieren
(Schrittweite z. B. 2 ¢ oder 3 % relativ), bis zum Floor.
5. Floor erreicht und kein Fill → Position halten, **Threema-Benachrichtigung**
senden (`ThreemaService` im Core existiert) und Position als „ExitPending"
markieren.
6. Position darf **nicht mehr optimistisch** aus `account.OpenPositions`
entfernt werden. Stattdessen Flag `ExitPending` (neues Property auf
`Position` oder Tracking-Dictionary im `CopyTradingState`), damit Limits
weiterhin korrekt rechnen und kein Doppel-SELL entsteht. Entfernen erst,
wenn der Fill über Sync/User-Channel (Phase 1) bestätigt ist.
**Preis-/Stufenlogik als pure statische Funktion** implementieren (z. B.
`SellLadder.NextPrice(referencePrice, step, floor, attempt)`) und mit
Unit-Tests abdecken.
**Akzeptanzkriterien:**
- Kein Code-Pfad sendet mehr `"MARKET"`-SELLs mit 0.01-Limit.
- Unit-Tests für Ladder-Preise (HF/normal, Floor-Clamping, 0.01/0.99-Grenzen).
- Log zeigt pro SELL: Referenzpreis, gewähltes Limit, Stufe.
### 0.2 Fee-Modell einführen
1. Fee-Rate je Markt beschaffen: Die CLOB-/Gamma-API liefert Fee-Informationen
am Markt-Objekt (Feldname bei Umsetzung anhand
https://docs.polymarket.com/trading/fees verifizieren, z. B. `fee_rate_bps`).
Fallback: statische Kategorie-Tabelle (Sports 0.75 %, Politics/Finance 1.0 %,
Crypto 1.8 %, Geopolitics 0 %).
2. `MarketData` (Core) um `TakerFeeBps` erweitern (EF-Migration Core),
Befüllung über `MarketSyncService` bzw. beim Markt-Fetch.
3. Risk-Check in `CopyTradingEngine`: erwartete Fee vom verfügbaren Edge
abziehen; Mikro-Trades, deren Fee den erwartbaren Gewinn frisst, verwerfen
(Logging mit Begründung wie bei den bestehenden Checks).
4. PnL-Berechnung (Demo **und** Live-Anzeige) um Fees korrigieren.
**Akzeptanz:** Fee erscheint im TradeReasoning-Log jedes BUY; Demo-PnL weist
Fees aus.
### 0.3 `ProfitTarget` implementieren oder entfernen
`CopyTradingAccountSettings.ProfitTarget` (Default 50.0) existiert in Settings,
DB und UI, wird aber **nirgends ausgewertet** (toter Knopf).
**Empfehlung: implementieren** als optionaler Take-Profit:
- Semantik: `0` = deaktiviert; sonst Prozent-Gewinnschwelle.
- Prüfung im 30-s-Live-Sync (`PollLiveAccountsAsync`): wenn
`CurrentPrice >= EntryPrice * (1 + ProfitTarget/100)` → Verkauf über die
Eskalationsleiter aus 0.1 (Startlimit = CurrentPrice), ExitReason
`"Profit Target"`.
- Zusammenspiel mit `PreRedeemLimit` beachten (beide können feuern —
PreRedeem hat Vorrang, da näher an 1.00).
### 0.4 Kleinere Konsistenz-Fixes
1. **20-Sekunden-Spam-Blockade** (`PendingOrderTimestamps`-Check am Anfang des
SELL-Pfads): blockiert aktuell auch legitime SELLs, wenn der Master < 20 s
nach dem Kauf aussteigt. Fix: Blockade nur für gleichgerichtete Orders
(BUY nach BUY), SELL nach BUY zulassen.
2. **`_state.GlobalPnl`**: wird in `PollLiveAccountsAsync`-Close-Pfaden addiert,
in `PollClosedAccountsAsync` nicht → Anzeige driftet. Vereinheitlichen.
3. **Demo-`ClosedTrade` ohne `TokenId`**: Im Demo-SELL-Pfad wird `TokenId` nicht
gesetzt (Preload von `_processedClosures` filtert auf `TokenId`). Setzen.
---
## Phase 1 — Marktdaten-Fundament (Core-Infrastruktur)
> Diese Phase gehört in **PolyTrader.Core** (`src/PolyTrader.Core/Streaming/`),
> nicht ins Modul — MarketMaking- und BundleArbitrage-Modul (separate Pläne)
> setzen sie voraus.
### 1.1 CLOB User-Channel (echte Fills in Echtzeit)
Polymarket bietet einen authentifizierten WSS-User-Channel, der Order-Events
(Platzierung, Teil-/Voll-Fill, Cancel) der eigenen Accounts pusht.
Endpoint/Protokoll bei Umsetzung verifizieren:
https://docs.polymarket.com (CLOB WSS, `user` channel; Auth via API-Key/
Secret/Passphrase — liegen je Account in `AccountState`).
Neuer Core-Service `ClobUserChannelService : BackgroundService`:
- Verbindet pro Live-Account, Auto-Reconnect mit Backoff (Muster von
`AlchemyWssClient` übernehmen).
- Publiziert Fill-Events intern (Event oder Channel), z. B.
`record OrderFillEvent(int AccountId, string TokenId, string OrderId, string Side, decimal Price, decimal Size, DateTime Ts)`.
Konsumenten im Copytrading-Modul:
- `Position.EntryPrice`/`Size` mit **echten Fill-Daten** aktualisieren
(heute: Limit-Preis als EntryPrice, Korrektur erst im 30-s-REST-Sync).
- SELL-Eskalationsleiter (Phase 0.1): Fill-Bestätigung beendet die Leiter.
- Neue Tabelle `ct_fill_log` (EF-Migration im Modul): SignalPrice, OrderPrice,
FillPrice, Latenz (Signal→Fill in ms), TraderId, AccountId, TokenId, Side.
→ Grundlage für Slippage-Statistik in Phase 3.
### 1.2 CLOB Market-Channel (Orderbücher live)
Neuer Core-Service `ClobMarketDataService`:
- Abonniert den öffentlichen `market`-Channel für eine dynamische Token-Liste
(Subscribe/Unsubscribe zur Laufzeit).
- Hält `OrderBookCache` (Best-Bid/Ask, Tiefe der obersten N Level, Timestamp).
- Interface für Konsumenten: `IOrderBookProvider.TryGetBook(tokenId, maxAgeMs)`.
- REST-Fallback `GET /book` über `PolymarketClobClient`, wenn kein Stream aktiv.
**Hinweis:** Im Modul existiert bereits ein `PolymarketWssClient` (Auto-Redeem).
Nicht verschieben/umbauen (Regression-Risiko), sondern den neuen Core-Service
parallel aufbauen; spätere Konsolidierung als separater Schritt.
### 1.3 Pre-Trade-Orderbuch-Check in der Engine
Vor jedem Live-BUY in `CopyTradingEngine.ProcessAccountOrderAsync`:
1. Buch holen (`IOrderBookProvider`, Fallback REST, Timeout ~150 ms —
bei Timeout Verhalten wie heute, nicht blockieren).
2. Checks (neue Settings in `CopyTradingAccountSettings`):
- `MaxSpreadPct` (Default z. B. 5 %): Spread größer → Skip mit Log.
- Tiefen-Check: liegt an unserem Limit-Preis genug Ask-Size für
`exactShares`? Wenn nein → Skip („Sniping-Verdacht: Liquidität bereits
konsumiert") statt teuer ins dünne Buch zu laufen.
**Akzeptanz Phase 1:** Fill-Log füllt sich mit echten Fills; TradeReasoning
zeigt Spread/Tiefe-Entscheidungen; kein messbarer Latenz-Nachteil im Hot-Path
(> 200 ms Zusatz wäre Regression).
---
## Phase 2 — SELL-Verfeinerung: Proportionalität
Heute (Proportionalitätsfilter in `CopyTradingEngine`, SELL-Pre-Flight):
verkauft der Master < 30 % seines Bestands → ignorieren; ≥ 30 % → **wir
verkaufen alles**. Information über gestaffelte Exits geht verloren.
**Ziel:** Verkaufsquote spiegeln.
1. Beim Öffnen einer Position den Master-Bestand zum Einstiegszeitpunkt
festhalten (`MasterSharesAtEntry`, im `CopyTradingState.MasterTraderPositions`
bzw. auf der Position persistieren).
2. Bei SELL-Signal: `sellRatio = signal.Size / masterSharesVorVerkauf` (wie
heute berechnet). Statt Voll-Exit: `sharesToSell = ourShares * sellRatio`.
3. Untergrenzen beachten: bleibt danach < Polymarket-Minimum (56 Shares) übrig
→ Voll-Exit statt Rest-Dust.
4. Kleiner Teilverkauf (< 10 %) weiterhin ignorieren (Rauschen von Day-Tradern),
Schwelle konfigurierbar (`MinSellRatioPct`).
5. Verkauf läuft immer über die Eskalationsleiter aus Phase 0.1.
Akzeptanz: Unit-Tests für die Ratio-Logik inkl. Dust-Grenzen; Logs zeigen
„Teilverkauf x % gespiegelt".
---
## Phase 3 — Trader-Intelligence (Auswahl automatisieren)
> Beim Copytrading entscheidet die Master-Auswahl über den Großteil des
> Ergebnisses. Diese Phase macht sie messbar und selbstkorrigierend.
### 3.1 Copy-PnL-Score („Kopierbarkeit")
Der `MasterTraderAnalyticsJob` misst heute den PnL des **Masters**. Relevanter
ist, was **wir** mit ihm verdient haben — inkl. unserer Slippage und Fees.
1. Neue Kennzahlen je Master aus `ct_`-Closed-Trades (`ICopyTradeLogRepository`,
Filter `SourceTraderId`, letzte 30 Tage):
- `CopyPnl30d`, `CopyProfitFactor` (Bruttogewinn/Bruttoverlust),
`CopyAvgPnlPerTrade`, `CopyTradeCount30d`.
- `AvgSlippagePct` aus `ct_fill_log` (Phase 1.1): Ø(FillPriceSignalPrice)/SignalPrice.
2. Felder auf `TrackedTrader` ergänzen (+ EF-Migration `mod_copytrading_trackers`),
Berechnung im `MasterTraderAnalyticsJob`, Anzeige in `MastersTradersView`.
3. **Achtung Metrik-Falle:** Winrate allein ist irreführend (Favoriten-Käufer
haben 95 % Winrate und können trotzdem negativ sein). Profit-Faktor und
Ø-PnL/Trade als primäre Sortierung in der UI.
### 3.2 Sniper-/Verhaltens-Metriken in den Analytics-Job
Portierung der Logik aus `analyze_snipers.py` (liegt im Projektroot) nach C#
in den `MasterTraderAnalyticsJob`:
1. Data-API-Activity je Master über volle 3 Tage paginieren (das Skript zeigt
das Pagination-Muster; API-Limit je Request beachten).
2. Kennzahlen: `MedianHoldMinutes`, `SellWithin5MinPct` (Anteil SELLs < 5 min
nach zugehörigem BUY), `SellCount3d`.
3. Schwellen (konfigurierbar): `SellWithin5MinPct > 50 %` → Master als Sniper
flaggen: Warn-Status in UI + Threema-Hinweis. Optional Auto-Pause (siehe 3.3).
### 3.3 Automatischer Kill-Switch je Master
Neue Modul-Settings (global, z. B. in `CopyTradingState` + Persistenz):
`AutoPauseEnabled`, `AutoPauseMinTrades` (z. B. 10), `AutoPauseDrawdownUsd`
oder `-Pct`.
Regel im Analytics-Job (läuft 2×/Tag — zusätzlich stündlicher Light-Check
sinnvoll): Copy-PnL der letzten N Trades unter Schwelle → `IsActive = false`,
`Reasoning` mit Begründung + Zeitstempel befüllen, Threema-Notification.
Reaktivierung bewusst nur manuell.
**Akzeptanz Phase 3:** UI zeigt Copy-Score-Spalten; ein simulierter
Verlust-Master wird automatisch pausiert (Test mit Demo-Daten).
---
## Phase 4 — Maker-Mode & Demo-Realismus
### 4.1 Maker-Einstieg für langsame Master
Für Master mit Haltedauern von Stunden/Tagen (SwissTony/RN1-Typ) ist der
3-Sekunden-Taker-Fill unnötig teuer (Fees + Spread). Neues Verhalten
(Flag je Trader, z. B. `Category == "HOLDER"` oder eigenes Bool `MakerEntry`):
1. BUY als GTC-Limit **auf** Best-Bid (oder Mid 1 Tick) statt über dem Ask.
2. Kein Fill nach T Minuten (konfigurierbar, z. B. 10) und Signal-Markt noch
im Preisband → auf Taker-Verhalten eskalieren oder verwerfen (Setting).
3. Fees: Maker zahlt 0 und sammelt ggf. Rebates — im Fee-Modell (0.2) abbilden.
### 4.2 Demo-Modus realistisch machen
Demo füllt heute zum Signalpreis ohne Slippage/Fees → Demo-Ergebnisse sind
systematisch geschönt und als Validierung neuer Master unbrauchbar.
Fill-Modell im Demo-Pfad der Engine:
`FillPreis = Signalpreis + halber Spread (aus IOrderBookProvider, Fallback
+1 ¢) `, Fee der Marktkategorie abziehen, beides im `ClosedTrade` ausweisen.
**Akzeptanz:** Demo- und Live-PnL desselben Masters weichen über 2 Wochen um
< 20 % relativ ab (grobe Plausibilität statt heutiger Systematik-Lücke).
---
## Offene Entscheidungen (vor Umsetzung mit Richard klären)
1. `SellFloorPct`-Default und Stufen-Timing der Eskalationsleiter (0.1).
2. `ProfitTarget`: implementieren (Empfehlung) oder Feld entfernen?
3. Auto-Pause: nur benachrichtigen oder hart deaktivieren? (Empfehlung: hart,
nachts passiert sonst genau das Falsche.)
4. Maker-Mode: als Trader-Flag oder automatisch aus `MedianHoldMinutes`
ableiten? (Empfehlung: automatisch ab z. B. Median > 60 min, manuell
überschreibbar.)
## Reihenfolge & Abhängigkeiten
```
Phase 0 (sofort, unabhängig)
└── Phase 1 (Core-Infra; parallel zu 0 möglich, Livegang nach 0)
├── Phase 2 (braucht 0.1-Leiter)
├── Phase 3 (braucht 1.1-Fill-Log für Slippage; Rest unabhängig)
└── Phase 4 (braucht 1.2-Orderbuch)
```