Files
PolyTraderSharp/docs/archiv/umsetzungsplaene/UMSETZUNGSPLAN-CopyTrading-Verbesserungen.md
T
RichardandClaude Opus 5 6218a04fe4 Eine Roadmap statt fuenfzehn Plandokumente; Altbestand ins Archiv
Der Status des Projekts stand verstreut in elf Umsetzungsplaenen, drei
Konzepten, der Linux-Analyse und dem Projektstand - teils widersprechend, teils
wochenlang veraltet. Ab jetzt gibt es genau eine Statusquelle.

docs/ROADMAP.md (neu):
- Alle Vorhaben in vier Stufen A bis D, plus technische Schuld und Verlauf.
  Die Stufen sind eine Reihenfolge, keine Termine: jede schafft die
  Voraussetzung fuer die naechste.
- Statuszeichen: erledigt / offen / blockiert (mit Ursache) / bewusst
  zurueckgestellt / Idee, nicht beschlossen. Damit ist das, was wir NICHT bauen
  wollen, sichtbar vorgehalten statt unauffindbar in einem Plan zu schlummern.
- Inhaltlich getragen, nicht nur verlinkt: je Vorhaben Ziel, Phasen,
  Akzeptanzkriterien, offene Entscheidungen und Leitplanken aus den Quelldokumenten.
- Sichtbar gemacht, was vorher zwischen den Dokumenten verborgen lag:
  CopyTrading Phase 1 ist der Engpass der gesamten Roadmap (MarketMaking und
  BundleArbitrage haben harte Voraussetzungen darauf), und die Sniper-Metriken
  aus Phase 3.2 sind ein Spezialfall des StrategieDrift-Fingerprints - zusammen
  bauen statt doppelt.

Archiv (docs/archiv/):
- 15 Dokumente verschoben (11 Umsetzungsplaene, 3 Konzepte, ANALYSE-Linux-Portierung).
  Sie bleiben die Bauanleitungen mit Code-Bezuegen, Risikotabellen und
  Begruendungen - eingefroren ist nur ihr Status.
- archiv/README.md ordnet jedes Dokument seinem Roadmap-Punkt zu.

Verweise nachgezogen - der eigentliche Aufwand:
- 25 Markdown-Links repariert. 15 davon verschiebungsbedingt (eine Ebene
  tiefer), der Rest war schon vorher falsch: die Ideensammlung verlinkte
  Quellcode relativ zum Repo-Wurzelverzeichnis statt zu docs/.
- 12 Dateien ausserhalb von docs/ verwiesen in Kommentaren auf die Plaene
  (csproj, props, setup.json, sechs Quelldateien) - alle auf archiv/ umgebogen.
- Verweise auf Dateien, die der Fruehjahrsputz geloescht hat (Ui/,
  Program.cs, WindowMenuBar), zu Klartext entschaerft statt tote Links zu lassen.
- Gegenprobe: 85 Links geprueft, 0 kaputt. Build gruen, 476 Tests gruen.

PROJEKTSTAND.md entdoppelt: Abschnitt "Offen" verweist jetzt auf die Roadmap.
Arbeitsteilung ist damit klar - Projektstand sagt was IST, Roadmap was KOMMT.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 18:56:50 +02:00

15 KiB
Raw Blame History

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/feesbei 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:

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)