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>
15 KiB
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:
AlchemyWebsocketServiceerkennt On-Chain-Events der Master-Wallets (WSS).TraderMonitorService.TriggerFastBlockchainPoll()parst die Transaktion direkt (Fast Track, ~3–4 s hinter dem Master) oder fällt auf Data-API-Polling zurück.- Signale (
CopySignal) laufen über einen Channel in dieCopyTradingEngine. - Die Engine prüft Risiko-Limits (
CopyTradingAccountSettings: PerMarketLimit, PerMasterLimit, Zeitfenster-Limits, MaxBuyPrice) und platziert CLOB-Orders überPolymarketClobClient(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)
.agents/rules/clob.mdbeachten: Änderungen an der CLOB-Integration sind hochkritisch. Vor jeder Änderung Backup/Commit der alten Version, jede Änderung mehrfach prüfen.- Jede Phase lässt die App baubar und lauffähig zurück (Debug-Build grün).
- Entscheidungslogik als testbare, pure Funktionen extrahieren und in
PolyTrader.Tests(xUnit, existiert bereits) abdecken. - 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 694–735)
werden SELLs als "MARKET" mit Fallback-Limit 0.01m gesendet:
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
- Referenzpreis =
signal.Price(Exit-Preis des Masters). - Erste Order: GTD-Limit.
- HF-Trader (
trader.Category == "HF"):signal.Price - 0.005m. - Sonst:
signal.Price * (1 - settings.MaxPriceDifference / 100m).
- HF-Trader (
- Neuer Setting-Wert
SellFloorPctinCopyTradingAccountSettings(Default z. B. 15 %): absolute Untergrenze =signal.Price * (1 - SellFloorPct/100). - Hintergrund-Loop (Erweiterung von
CleanupStaleOpenOrdersAsyncinTraderMonitorServiceoder 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. - Floor erreicht und kein Fill → Position halten, Threema-Benachrichtigung
senden (
ThreemaServiceim Core existiert) und Position als „ExitPending" markieren. - Position darf nicht mehr optimistisch aus
account.OpenPositionsentfernt werden. Stattdessen FlagExitPending(neues Property aufPositionoder Tracking-Dictionary imCopyTradingState), 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
- 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 %). MarketData(Core) umTakerFeeBpserweitern (EF-Migration Core), Befüllung überMarketSyncServicebzw. beim Markt-Fetch.- 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). - 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): wennCurrentPrice >= EntryPrice * (1 + ProfitTarget/100)→ Verkauf über die Eskalationsleiter aus 0.1 (Startlimit = CurrentPrice), ExitReason"Profit Target". - Zusammenspiel mit
PreRedeemLimitbeachten (beide können feuern — PreRedeem hat Vorrang, da näher an 1.00).
0.4 Kleinere Konsistenz-Fixes
- 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. _state.GlobalPnl: wird inPollLiveAccountsAsync-Close-Pfaden addiert, inPollClosedAccountsAsyncnicht → Anzeige driftet. Vereinheitlichen.- Demo-
ClosedTradeohneTokenId: Im Demo-SELL-Pfad wirdTokenIdnicht gesetzt (Preload von_processedClosuresfiltert aufTokenId). 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/Sizemit 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überPolymarketClobClient, 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:
- Buch holen (
IOrderBookProvider, Fallback REST, Timeout ~150 ms — bei Timeout Verhalten wie heute, nicht blockieren). - 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.
- Beim Öffnen einer Position den Master-Bestand zum Einstiegszeitpunkt
festhalten (
MasterSharesAtEntry, imCopyTradingState.MasterTraderPositionsbzw. auf der Position persistieren). - Bei SELL-Signal:
sellRatio = signal.Size / masterSharesVorVerkauf(wie heute berechnet). Statt Voll-Exit:sharesToSell = ourShares * sellRatio. - Untergrenzen beachten: bleibt danach < Polymarket-Minimum (5–6 Shares) übrig → Voll-Exit statt Rest-Dust.
- Kleiner Teilverkauf (< 10 %) weiterhin ignorieren (Rauschen von Day-Tradern),
Schwelle konfigurierbar (
MinSellRatioPct). - 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.
- Neue Kennzahlen je Master aus
ct_-Closed-Trades (ICopyTradeLogRepository, FilterSourceTraderId, letzte 30 Tage):CopyPnl30d,CopyProfitFactor(Bruttogewinn/Bruttoverlust),CopyAvgPnlPerTrade,CopyTradeCount30d.AvgSlippagePctausct_fill_log(Phase 1.1): Ø(FillPrice−SignalPrice)/SignalPrice.
- Felder auf
TrackedTraderergänzen (+ EF-Migrationmod_copytrading_trackers), Berechnung imMasterTraderAnalyticsJob, Anzeige inMastersTradersView. - 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:
- Data-API-Activity je Master über volle 3 Tage paginieren (das Skript zeigt das Pagination-Muster; API-Limit je Request beachten).
- Kennzahlen:
MedianHoldMinutes,SellWithin5MinPct(Anteil SELLs < 5 min nach zugehörigem BUY),SellCount3d. - 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):
- BUY als GTC-Limit auf Best-Bid (oder Mid − 1 Tick) statt über dem Ask.
- Kein Fill nach T Minuten (konfigurierbar, z. B. 10) und Signal-Markt noch im Preisband → auf Taker-Verhalten eskalieren oder verwerfen (Setting).
- 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)
SellFloorPct-Default und Stufen-Timing der Eskalationsleiter (0.1).ProfitTarget: implementieren (Empfehlung) oder Feld entfernen?- Auto-Pause: nur benachrichtigen oder hart deaktivieren? (Empfehlung: hart, nachts passiert sonst genau das Falsche.)
- Maker-Mode: als Trader-Flag oder automatisch aus
MedianHoldMinutesableiten? (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)