- IModuleUiHost (Core) um Navigation erweitert: Views/IsOpen/OpenView/ActivateMain/OpenStateChanged
+ optionales Icon je ModuleView. So kann JEDES Fenster (auch Modul-Fenster, die nur Core kennen)
das gemeinsame Fenster-Menue bauen.
- WindowMenu (Core): baut das 'Fenster'-Dropdown (Launcher + alle Views + Beenden), haakt offene
Fenster an, markiert das aktuelle fett; Neuaufbau beim Aufklappen.
- ShellUiHost: SetMainWindow/ActivateMain; oeffnet ALLE Fenster jetzt MAXIMIERT (Vorgabe).
- Launcher: alle Modul-/Core-Buttons STATISCH im Designer (btn_copytrading/-resolutionfarming/
-supervisor ergaenzt), an View-IDs gebunden; fehlt eine View -> Button deaktiviert. Dynamischer
Laufzeit-Anhang ENTFERNT -> kein doppelter Accounting-Button mehr; leerer btn_accounting_Click raus.
Datei/Beenden -> Fenster-Menue (WindowMenu). Launcher startet maximiert (Sizable, MinSize 1280x720).
Build 0 Fehler, --smoke-ui alle 6 Views + Launcher gruen. Fenster-Menue auf den uebrigen Fenstern folgt (1b).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RF-UI designerfaehig (Richards Vorgabe, letzter code-only-Altbestand):
- ResolutionFarmingMainForm auf partial + .Designer.cs umgestellt (4 Tabs Kandidaten/
Positionen/Historie/Settings, alle Controls im Designer; Verhalten unveraendert).
S-3 Counterfactual ('Was waere aus abgelehnten BUYs geworden?'):
- CounterfactualJob (alle 6h, API-gedrosselt): nimmt Rejected-BUY-Entscheidungen mit
abgelaufenem MarketEndDate aus dem Journal, prueft die Marktaufloesung
(ICounterfactualResolutionSource; live = Adapter um CheckMarketResolutionAsync) und
speichert IsWinner + hypothetischen PnL/Share (CounterfactualMath, pur) nach
sup_counterfactuals (unique je DecisionId -> idempotent). Migration generiert+angewendet.
- Agent-Tool query_counterfactuals + UI-Tab 'Counterfactual' (via Designer).
S-3 Threema-Tagesbericht:
- DailyReportService: taeglich zur konfigurierten Stunde (OPT-IN via
POLYTRADER_SUPERVISOR_DAILY=0-23) laesst der Agent einen 24h-Kurzbericht erstellen
(KPIs je Modul, Fehler, Reject-Haeufungen), sendet via Threema und legt ihn als
sup_report ab. Ohne OpenRouter-Key: stiller Skip mit Log.
Tests: +7 (CounterfactualMath, Job aufgeloest/unaufgeloest/idempotent, Tagesbericht
mit/ohne Key). Build 0 Fehler, 367 Tests gruen, --smoke-ui alle 5 Views gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Das Dashboard ist keine stumpfe Trade-Liste mehr, sondern ein Auswertungs-Fenster:
- ToolStrip (via Designer): Scope-Filter Konto/Modul/Live-Demo/Zeitraum + Aktualisieren.
- TabControl mit 2 Tabs:
- Dashboard: KPI-Kacheln (Netto-PnL/Winrate/Trades/O-PnL/Profit-Faktor) + 3 Charts
(Equity-Kurve, PnL je Modul, PnL je Tag) fuer den gewaehlten Scope.
- Tradehistorie: gefilterte Trade-Liste (Spalten via Designer) + Suche + Gewinner/Verlierer.
- Charts via ScottPlot CORE-Paket (nur SkiaSharp, .NET-nativ) -> als Bitmap in PictureBoxen
gerendert; KEINE OpenTK/.NET-Framework-Transitiven (bewusst nicht ScottPlot.WinForms).
- Auswertungslogik pur in TradeAnalytics (getestet). In-Memory-Filter auf gecachtem Recent-Set.
- Smoke-UI konstruiert die DashboardView jetzt direkt -> verifiziert das Chart-Rendering headless.
Build 0 Fehler, 331 Tests gruen, --smoke-ui: [OK] core.dashboard konstruiert (inkl. Charts).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- TerminalView: ContextMenuStrip (Kopieren/Alles auswaehlen/Alles kopieren/Terminal leeren)
am rtbTerminal - macht das Kopieren entdeckbar (Ctrl+C funktioniert zusaetzlich nativ).
- JobsView: ToolStrip 'toolStripJobs' (docked Top) mit Starter-Button 'Aktualisieren'
(dgvJobs.Refresh) - weitere Job-Steuerelemente folgen nach und nach.
Beide via Designer (.Designer.cs), Logik im Code-Behind. Build 0 Fehler, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Settings: neuer Toolbar-Button 'Master-Key erzeugen' (Tab General Settings, via Designer).
Erzeugt zufaelligen 32-Byte-AES-Key -> master.key (gitignored), nur aktiv wenn KEIN Key
existiert (Env-Var oder Datei), Lockout-Schutz + Backup-Warnung, danach deaktiviert.
- Einbezogen: laufende Designer-Umstrukturierung (SettingsView/LauncherForm: Button-Bilder
aus Properties.Resources statt eingebettet; dgv_accountlist im Launcher; DashboardView.resx).
- docs/steuer: US-CPA-Fragebogen als PDF.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- F5: Startwarnung, wenn der DB-Connection-String kein SslMode erzwingt (String selbst wird
nie geloggt). Eure Connection enthaelt bereits SslMode -> Warnung bleibt aus.
- F6: geprueft - keine Secret-Werte in Logs (nur Vorhandensein-Flags/Fehlermeldungen).
- Sicherheitskonzept: Status F1-F7 dokumentiert.
Build 0 Fehler, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Behebt den kritischsten Befund (Klartext-Private-Keys in remote-gehosteter MySQL):
- SecretProtection (Core/Security): AES-256-GCM, authenticated. Master-Key AUSSERHALB der DB
(env POLYTRADER_MASTER_KEY, sonst gitignorierte master.key). Format enc:v1:base64(nonce|tag|ct).
Alt-Klartext (ohne Praefix) wird gelesen und beim Speichern verschluesselt (selbstheilend).
Ohne Master-Key: Passthrough + deutliche Startwarnung (kein stiller Sicherheitsverlust).
- EncryptedStringConverter (EF ValueConverter) auf core_accounts.PrivateKey/ApiSecret/ApiPassphrase;
Spalten 256->512 verbreitert (Migration EncryptAccountSecretsWidenColumns, offline generiert).
- Program.cs: Master-Key vor der Hydration laden; nach Start einmalige/idempotente Re-Encryption
vorhandener Klartext-Credentials. Auch in --smoke-ui verdrahtet.
- CoreDbContextFactory nutzt jetzt fixe Server-Version (offline-Migrationsgenerierung, kein DB-Zugriff).
13 neue Krypto-Tests (Round-Trip, Nonce-Frische, Manipulations-/Falscher-Key-Erkennung, Passthrough,
Key-Formate). Build 0 Fehler, 324 Tests gruen, --smoke-ui ok (Warnung ohne Key wie erwartet).
AKTIVIERUNG (im Zielland): POLYTRADER_MASTER_KEY setzen (zufaelliger 32-Byte-Base64-Key, SEPARAT sichern!)
+ Migration anwenden (dotnet ef database update --context CoreDbContext). Danach Alchemy-/Mullvad-Secrets
aus F3 rotieren. WICHTIG: Master-Key-Verlust = Kein Zugriff auf die Keys mehr.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der auskommentierte redeem_markets.py-Aufruf uebergab Private-Key + Secrets als
Prozess-Argumente (in der Prozessliste sichtbar). War deaktiviert, aber latentes Risiko
(reaktivierbar/falsches Muster) -> entfernt. Sicherheitshinweis fuer kuenftiges
Auto-Redeem hinterlegt (Secrets nie via argv; Redeem in .NET/Nethereum).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
F4: Newtonsoft.Json in der Threema-Lib von 11.0.2 (Advisory NU1903, high) auf 13.0.4
gehoben (= App-Version). Build ohne NU1903, 311 Tests gruen.
F3: hardcodierte Secrets aus ServerSettings-Defaults entfernt (Alchemy-API-Key in
PolygonRpcUrl, Mullvad-Account-ID) -> leere Defaults; echte Werte kommen aus
server_settings.xml (gitignored). WICHTIG (nicht im Code moeglich): beide Secrets
ROTIEREN, da sie in der Git-History liegen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Phase RF-1 (read-only): Scanner holt bald aufloesende Favoriten von einer
IFarmingMarketSource, bewertet sie und schreibt JEDEN Kandidaten (akzeptiert wie
abgelehnt inkl. Grund) nach rf_candidates. Platziert keine Orders.
- MarketScannerService.Evaluate (pur/statisch, voll getestet): Filterkette mit
Reject-Grund = erster Fehlschlag (Preisband -> Kategorie -> Blacklist ->
Aufloesungsfenster -> Netto-Edge nach Fees). Cluster-Key + Score immer berechnet.
- ScanAccountAsync: Beschaffung -> Bewertung -> Persistenz. BackgroundService-Loop
(12min) ueber Accounts mit Settings; fehlertolerant.
- IFarmingMarketSource + ScannedMarket-DTO trennen die (live-/API-gebundene)
Beschaffung von der Bewertung -> Pipeline ohne echte Gamma/CLOB-API testbar.
- NullFarmingMarketSource als Default: Modul laeuft ohne Live-Anbindung (die im
Zielland registriert wird) und produziert dann korrekt keine Kandidaten.
Hinweis: Zur Laufzeit fragt der Scanner rf_settings ab; bis die Migration angewendet
ist, faengt der try/catch den fehlenden-Tabelle-Fehler ab (nur Log). Migration bewusst
separat anzuwenden.
7 neue Tests (Evaluate-Faelle + Orchestrierung). Build 0 Fehler, 296 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Entities RfCandidate/RfPosition/RfClosedTrade (+ RfSettings aus Slice 1).
- ResolutionFarmingDbContext: Tabellen rf_settings/rf_candidates/rf_positions/
rf_closed_trades. Autoincrement-PKs (Identity) fuer Candidate/ClosedTrade von
Anfang an (Lehre aus dem CopyTrading-TradeId-Problem), zusammengesetzter PK
(AccountId,TokenId) fuer Positions, Indizes + Decimal-Precision.
- 4 Repos (Settings/Candidate/Position/ClosedTrade) mit serverseitigen Aggregaten
(RealizedPnlSince fuer Kill-Switch, CountOpenedSince fuer Tages-Drossel).
- Modul registriert DbContextFactory + Repos.
- Design-Time-Factory nutzt die fest gepinnte Server-Version -> Migration wurde
OHNE DB-Verbindung generiert (kein Zugriff auf die produktive DB). Anwenden per
'dotnet ef database update' bewusst im Zielland/lokal durch den Nutzer.
6 neue EF-InMemory-Repo-Tests. Build 0 Fehler, 289 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vorbereitung fuer das ResolutionFarming-Modul: FeeModel von
PolyTrader.Modules.CopyTrading.Logic nach PolyTrader.Core.Trading verschoben, damit
mehrere Strategiemodule dieselbe Fee-Logik nutzen koennen (Module duerfen nicht
voneinander abhaengen -> geteiltes gehoert in den Core). CopyTrading-Referenzen +
FeeModelTests auf den Core-Namespace umgestellt. Rein mechanisch, verhaltensneutral.
Build 0 Fehler, 244 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verifiziert den kritischen K1a-Fix mit Integrationstests statt nur per Review
(nach dem K3-Fehlplatzierungs-Fund):
- TraderMonitorService._clob -> IClobClient (verhaltensneutral); CleanupStaleOpenOrdersAsync
internal. FakeCopyTradeLogRepository ergaenzt.
- 2 Tests: Stale Order mit aktiver Leiter wird NICHT gecancelt; ohne Leiter WIRD sie
storniert (Kontrast, beweist Nicht-Vacuositaet).
Build 0 Fehler, 244 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Testabdeckung fuer den geldkritischen Engine-Pfad (bisher 0 Tests), da die
Fable-Guards zwischen Services entstehen und Unit-Tests sie nicht fangen:
- Engine._clob -> IClobClient (verhaltensneutral, DI registriert IClobClient bereits);
ProcessAccountOrderAsync internal. FakeMarketRepository/FakeAccountRepository ergaenzt.
- 7 Integrationstests (CopyTradingEngineTests) ueber gemockten CLOB: H3 BUY-Skip bei
ExitPending, Doppel-SELL-Guard, K3 System-Close (TraderId==0) schliesst Fremd-Position,
Fremd-Trader-SELL bleibt abgewiesen, H2 Cleanup schont Leiter (+ Kontrast ohne Leiter).
DABEI ECHTEN BUG GEFANGEN: Der K3-Fix aus Slice 3 sass an der falschen Stelle
(IsAuthorizedSell nach dem Position-Remove, Zeile ~643) – der eigentliche Ownership-Check
ist der fruehe inPortfolio-Lookup (Zeile 437, p.SourceTraderId == signal.TraderId), der
System-Signale schon vorher mit early return abwies. Fix jetzt am richtigen Ort; der
downstream-Check bleibt als Defense-in-depth. Ohne den Engine-Test waere das unentdeckt geblieben.
Build 0 Fehler, 242 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entscheidung Richard (Variante A): Re-buyt der Master, waehrend unsere SELL-Leiter
die Position abverkauft, wird der BUY uebersprungen statt zugekauft. Sonst stockte
der BUY die Position auf, die die Leiter danach inkl. neuer Shares zum alten Floor
verkauft (oekonomisch widerspruechlich). Die Leiter fuehrt den Exit sauber zu Ende.
Guard am Anfang des Engine-BUY-Pfads (spiegelt den bestehenden SELL-ExitPending-Guard).
Build 0 Fehler, 233 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
H4 – Rundungs-Dust-Reject-Schleife:
- SellLogic.RoundToTick (0.001, AwayFromZero, wie CalculateExactOrderAmounts). Die
Leiter platziert Preise gerundet -> usdc = size × Preis geht exakt auf (kein Dust).
- ProcessLadderAsync bricht ab, wenn pos.Size < MinShares (Dust-Rest gehalten,
ExitPending=false), statt endlos Sub-Minimum-Orders zu schicken.
M1 – GlobalPnl-Doppelzaehlung: in beiden Live-Close-Bloecken jetzt NUR innerhalb des
_processedClosures-Dedup-Guards gebucht (API-Lag zaehlte sonst doppelt).
M2 – TokenId in beiden Live-Close-Records gesetzt (sonst greift die Dedup nach Neustart nicht).
M3 (Minimum) – TradeId-Init: serverseitiges GetMaxTradeId() statt Full-Table-Find(_=>true).Max();
Fehlschlag wird laut geloggt statt still geschluckt. (Autoincrement-Migration bewusst als
Follow-up aufgeschoben – Schema-Aenderung an der Trade-Persistenz erst im Zielland live verifizieren.)
M4 – MongoExportParser ProfitTarget-Default 50 -> 9999 (Take-Profit bleibt dormant).
M6 – Fee-Satz (real oder Kategorie-Fallback, FeeModel.ResolveBps) in alle signierten Orders:
Engine-BUY, SELL-Leiter (Start/Step/Floor), PreRedeem (WSS + REST). API-gated, im Zielland verifizieren.
Doku – stale [Description]: ProfitTarget/SellFloorPct als IMPLEMENTIERT markiert.
Tests: +10 (RoundToTick, Dust-Abbruch, ResolveBps). Build 0 Fehler, 233 gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
K3 – Demo-Auto-Close bei Marktauflösung funktioniert wieder:
- System-SELLs (TraderId==0) sind vom Ownership-Check ausgenommen. Regel in reine
CopyTradingRisk.IsAuthorizedSell(signalTraderId, positionSourceTraderId) extrahiert
(System ODER Besitzer) und in der Engine genutzt. Vorher lehnte der Check jedes
System-Close ab (Copy-Positionen haben SourceTraderId>0) -> Demo-Positionen
schlossen bei Resolution nie, 30s-Loop feuerte endlos.
- Resolved-Cache (_resolvedDemoMarkets) in PollDemoExpirationsAsync: aufgeloeste
Maerkte werden nicht erneut angefragt (kein Log-Spam / keine unnoetige API-Last).
M5 – Auto-Pause in der Demo-Phase nicht mehr wirkungslos:
- Anzeige-Spalten (CopyPnl30d etc.) zeigen Live-Kennzahlen, sobald vorhanden, sonst
Demo-Kennzahlen -> Demo-Validierung ist sichtbar statt leer.
- Harte Auto-Pause wirkt bewusst NUR auf Live-Ergebnissen.
- Kadenz: stuendlicher Light-Check (Auto-Pause) statt nur alle 12h; schwere
History-Analyse bleibt 12h (schliessen sich pro Tick aus, kein Doppellauf).
Tests: 5 neue (IsAuthorizedSell). Build 0 Fehler, 223 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ruhende GTC-Leiter-/MakerEntry-Orders ueberleben Neustarts auf dem CLOB, der
Verwaltungszustand (ExitLadders/ExitPending/PendingOrderTimestamps) ist transient.
Ohne Bereinigung liefe die Engine gegen Waisen-Orders (Doppel-Leiter, Kaskaden).
- StartupOrderReconciliationService (IHostedService): storniert beim Start je
Live-Account alle offenen CLOB-Orders; danach entscheidet die Engine sauber neu.
Registriert als ERSTER Modul-HostedService (nach Hydration, vor Monitor/Engine),
pro Account fehlertolerant.
- GetOpenOrdersAsync: assetId jetzt optional (default "") -> ohne Filter ALLE
offenen Orders des Accounts. Signaturneutral (HMAC geht ueber Pfad ohne Query),
rueckwaertskompatibel fuer die bestehenden per-Asset-Aufrufer. IClobClient +
FakeClobClient nachgezogen.
Hinweis: "/data/orders ohne asset_id = alle Orders" ist API-gated und im Zielland
live zu verifizieren (wie M6).
Tests: 3 neue (cancelt alle Orders je Live-Account, ueberspringt Demo/credential-los,
no-op ohne offene Orders). Build 0 Fehler, 218 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wer darf die ruhende Leiter-Order anfassen? Nur die Leiter selbst.
- H1 (Race): StartLadderAsync macht ZUERST einen atomaren Claim
(ExitLadders.TryAdd). Master-SELL (Engine) und Profit-Target (Sync) sind
damit idempotent – kein Doppel-GTC / keine Fehlerkaskade. Verliert ein
Aufrufer den Claim, wird die (evtl. von der Engine entfernte) Position als
ExitPending zurueckgestellt – kein Waise. Order-Fehler gibt den Claim zurueck.
- K1a (Floor-Deadlock): CleanupStaleOpenOrdersAsync ueberspringt Keys mit
aktiver Leiter (ExitLadders.ContainsKey) – cancelt die Floor-Order nicht mehr.
- K1b (Floor-Robustheit): ProcessLadderAsync prueft am Floor via GetOpenOrders,
ob die SELL-Order noch ruht; falls nicht (Cleanup/extern/Teil-Fill), neu
platzieren statt bis zum Neustart unverkaeuflich zu haengen.
- H2 (Engine-Cancel): Pre-Signal-CancelConflictingOrders wird uebersprungen,
wenn fuer den Markt eine Leiter aktiv ist – zerschiesst die Leiter-Order nicht.
Tests: 8 neue Integrationstests (SellLadderService ueber gemockten IClobClient):
atomarer/paralleler Claim, Waisen-Schutz, Claim-Rueckgabe, Floor-Neuplatzierung,
Floor-Halten bei ruhender Order, Leiter-Ende bei gefuellter Position.
Verifikation: Build 0 Fehler, 215 Tests gruen, --smoke-ui ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Ist TrackedTrader.MakerEntry gesetzt, wird der BUY OHNE Preisaufschlag als
ruhendes GTC-Limit am Master-Preis platziert (statt teurem Taker-Fill via GTD),
spart Fees/Spread bei langsamen Mastern. Nutzt die getestete
CopyTradingRisk.CalculateBuyOrderPrice (0 % Aufschlag). Default aus -> dormant.
(Ohne Orderbuch ist der Master-Preis die Naeherung fuer den Bid; echte
Bid-Platzierung + Nichtfill-Eskalation folgen mit dem Market-Channel/Zielland.)
207 Tests gruen. Build/Smoke gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SellLogic.IsProfitTargetReached (pure, getestet): currentPrice >= entry*(1+pct/100);
pct<=0 oder Default 9999 = inaktiv.
- Ladder-Start-Logik konsolidiert: SellLadderService.StartLadderAsync ist jetzt die
gemeinsame Quelle fuer Master-SELLs (Engine) UND eigene Exits (Profit-Target).
SellLadderService als Singleton+Hosted registriert; Engine + TraderMonitor
injizieren es. Engine-SELL-Block ruft nur noch StartLadderAsync (verhaltensgleich).
- TraderMonitorService.CheckProfitTargetsAsync im 30s-Live-Sync: erreicht eine
Live-Position ihre Schwelle, Exit ueber die Leiter (Startlimit = aktueller Preis,
ExitReason "Profit Target"). PreRedeemLimit hat Vorrang. Dormant, da ProfitTarget
projektweit 9999.
169 Tests gruen. Build/Smoke gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SELL-Spam-Blockade greift nur noch bei kürzlichem SELL: PendingOrderTimestamps
bekommt eine Side ("BUY"/"SELL"); ein vorheriger BUY blockiert einen legitimen
schnellen SELL des Masters nicht mehr (alle Schreibstellen aktualisiert).
- GlobalPnl: PollClosedAccountsAsync addiert den realisierten PnL jetzt auch
(wie PollLiveAccountsAsync) -> Anzeige driftet nicht mehr.
- Demo-ClosedTrade erhält TokenId (der _processedClosures-Preload filtert darauf;
fehlte -> Duplikat-/Dedup-Risiko nach Neustart).
163 Tests gruen. Build/Smoke gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Behebt die April-Verlustquelle: SELLs wurden als Market-Order mit 0.01-Limit ins
oft leergeraeumte Orderbuch geworfen -> wir wurden zur Exit-Liquidity. Jetzt:
GTC-Limit nahe am Master-Exit, stufenweises Nachpreisen bis zum Floor.
- Position.ExitPending (runtime-only, EF-ignoriert): Position wird bei SELL NICHT
mehr optimistisch entfernt, sondern als ExitPending zurueckgestellt (kein
Doppel-SELL, Limits rechnen korrekt; Sync schliesst nach bestaetigtem Fill).
- CopyTradingState.ExitLadders + ExitLadderState (transienter Leiter-Zustand).
- SellLogic (pure, getestet): FirstLimit (HF-fest/prozentual), Floor (SellFloorPct),
NextPrice (relative Stufe, auf Floor geclamped), IsAtFloor, LadderStepPct (3%),
LadderIntervalSeconds (HF 20s / sonst 120s).
- SellLadderService (BackgroundService): senkt offene Exit-Limits stufenweise
(cancel via CancelConflictingOrdersAsync -> tiefer neu platzieren), am Floor ohne
Fill Position halten + Threema-Benachrichtigung. Fills erkennt der bestehende Sync.
- CopyTradingEngine SELL-Live-Pfad ruft die Leiter; Demo-Pfad unveraendert.
Doppel-SELL-Guard ueber ExitPending. Umfangreiches Logging (kein Live-Test moeglich).
163 Tests gruen. Build/Smoke gruen. Backup-Rollback: Commit 1dffc9e.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Neue pure Klasse TradeMath (Logic/): ExitValue, RealizedPnl, PnlPercent
(Null-Guard), WeightedAverageEntryPrice (Div-durch-0-Schutz).
- CopyTradingEngine ruft sie jetzt im Demo-Close-PnL und bei der Positions-
Aufstockung (Demo + Live, gewichteter Einstiegspreis) – verhaltensneutral.
- TradeMathTests (6 Faelle: Gewinn/Verlust, %-Basis, 0-Kapital/0-Size-Guards).
Gesamt 160 Tests gruen. Build/Smoke gruen. (Phase-0.1-SELL-Wiring weiterhin
bewusst zurueckgestellt bis zum Server-Test; pure Leiter liegt getestet bereit.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>