API audit: expose 7d/24h windows, remove broken repair-db, add API docs
- TraderDetailDto now exposes PnL7d/WinRate7d/PnL24h/WinRate24h and CurrentBalance — the engine has computed these all along but the API never delivered them. - Removed POST /api/dev/repair-db: its raw SQL referenced non-existent columns/tables (Trades.Type/Payout, Traders.LastPositionsUpdatedAt, table "Jobs") and would have deleted ALL TraderPositions including pruned-history conserves. The supported repair path is the WinForms "Recalculate All Traders" action. - Swagger tags for Jobs and Dev groups; full endpoint reference in docs/API.md (kept generic — external consumers like PolyTrader adapt to our API, not vice versa). - FIXPLAN Teil E: master-selection gap analysis as generic extensions (profile endpoint, out-of-sample window metrics, price-band profile with per-band win rate, stop-loss ratio, copyability aggregates with category fees, correlation endpoint, martingale trait). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
28e112f128
commit
a1fcb4ace5
@@ -506,3 +506,105 @@ gegen 300+/min) — das Trade-Replay-PnL ist für diese Klasse bereits falsch un
|
|||||||
4. Tägliches DB-Wachstum sichtbar reduziert (DB-Size-Anzeige im WinForms-Statusbar beobachten).
|
4. Tägliches DB-Wachstum sichtbar reduziert (DB-Size-Anzeige im WinForms-Statusbar beobachten).
|
||||||
|
|
||||||
Reihenfolge: **D1 → D2/D2b/D2c → D3** (D2c ist klein und gehört in denselben Engine-Durchlauf wie die Winrate; bei D3 zuerst Tier C, dann Tier B).
|
Reihenfolge: **D1 → D2/D2b/D2c → D3** (D2c ist klein und gehört in denselben Engine-Durchlauf wie die Winrate; bei D3 zuerst Tier C, dann Tier B).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Teil E — Master-Auswahl-Metriken & generischer Profil-Endpoint (ergänzt 2026-07-11)
|
||||||
|
|
||||||
|
> Hintergrund: Ein externer Konsument (PolyTrader-Copytrading) braucht eine belastbare
|
||||||
|
> Master-Trader-Auswahl (Prüfplan liegt in `PREDICTALYTICS-PRUEFPLAN-Master-Auswahl.md` im
|
||||||
|
> PolyTraderSharp-Repo). **Bewusste Architektur-Entscheidung: Die API bleibt generisch.**
|
||||||
|
> Kein PolyTrader-spezifisches Format, kein „HOLDER/STOPLOSS"-Vokabular in der API —
|
||||||
|
> wir exponieren neutrale Metriken, der Konsument mappt selbst.
|
||||||
|
> Abgleich: M2/M8 existieren (D2c/CategoryPerformances), M3/M9 teilweise als Traits (D2),
|
||||||
|
> M1/M5/M6 teilweise; **komplett neu sind M4, M7, Out-of-Sample-Fenster, Kopierbarkeits-
|
||||||
|
> Aggregate und die Korrelations-Sicht** — das ist Teil E.
|
||||||
|
|
||||||
|
### E1. Generischer Profil-Endpoint `GET /api/traders/{id}/profile`
|
||||||
|
|
||||||
|
Ein Aufruf liefert das vollständige Analyse-Profil (statt 4+ Einzel-Calls):
|
||||||
|
Stammdaten + alle Analytics-Felder + Traits (D2) + Rendite-Metriken (D2c) +
|
||||||
|
Fenster-Blöcke (E2) + Fingerprint-Verteilungen (E3) + Kopierbarkeits-Aggregate (E5).
|
||||||
|
Nur persistierte Werte ausliefern (kein On-the-fly-Rechnen wie im Deep-Dive) —
|
||||||
|
der Endpoint muss schnell und pollbar sein. In `docs/API.md` dokumentieren.
|
||||||
|
|
||||||
|
### E2. Fenster-Metriken als Struktur + Out-of-Sample-Vergleich
|
||||||
|
|
||||||
|
**Problem:** Die 24h/7d/30d-Felder sind Einzelspalten; der Prüfplan braucht zwei frei
|
||||||
|
definierte Vergleichsfenster (z. B. Tag −180…−60 vs. −60…heute), um Glücks-Wallets
|
||||||
|
auszusortieren (nur wer in BEIDEN Fenstern liefert, ist ein Kandidat).
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
- Neue Tabelle `TraderWindowMetrics`: TraderId, WindowStart, WindowEnd, ClosedMarkets,
|
||||||
|
WinRate, AvgReturnPct (M1: Ø realisierte Rendite je Markt), MedianWinReturnPct,
|
||||||
|
MedianLossReturnPct, ProfitFactor, ComputedAt. Unique (TraderId, WindowStart, WindowEnd).
|
||||||
|
- Berechnung im Analytics-Lauf für zwei konfigurierbare Fenster
|
||||||
|
(`AnalysisWindows`-Sektion in appsettings, Default: −180…−60 und −60…0 Tage).
|
||||||
|
Wiederverwendet die D2c-Logik mit Zeitfilter auf den Markt-Abschlusszeitpunkt.
|
||||||
|
- Im Profil (E1) als `windows[]`-Array. **Achtung Retention:** Fenster A reicht weiter
|
||||||
|
zurück als 90 Tage — Berechnung muss mit fehlender Historie ehrlich umgehen
|
||||||
|
(`closedMarkets` klein → Konsument sieht die dünne Stichprobe). Nach der
|
||||||
|
Retention-Verlängerung auf 180 Tage (D3) wird Fenster A tragfähig.
|
||||||
|
|
||||||
|
### E3. Fingerprint-Verteilungen (persistiert, im Profil)
|
||||||
|
|
||||||
|
Im Analytics-Lauf berechnen und als JSON-Spalte(n) auf `TraderAnalytics` oder eigene
|
||||||
|
Tabelle persistieren:
|
||||||
|
- **Preisband-Profil (M7):** Einsatz-Anteil je 10-¢-Einstiegspreisband über Buys,
|
||||||
|
**plus realisierte Winrate je Band** (macht Glück von System unterscheidbar:
|
||||||
|
„kauft 90–95-¢-Shares, gewinnt 97 %" = +Edge sichtbar pro Band).
|
||||||
|
- **Haltedauer:** Median (nicht nur Ø) Stunden Kauf→Exit/Auflösung (M6).
|
||||||
|
- **Positionsgrößen:** P50/P90-Amount (M9-Basis).
|
||||||
|
- Trade-Frequenz je Woche (aus Trades30d ableitbar, im Profil ausgeben).
|
||||||
|
|
||||||
|
### E4. Exit-Verhalten klassifizieren (M4) — generisch als Trait + Kennzahl
|
||||||
|
|
||||||
|
- Kennzahl `stop_loss_ratio`: Anteil der Sells, die nach einem Preisrückgang von
|
||||||
|
≥ 10 % unter den Einstands-AvgCost erfolgen (Sell-Preis ≤ 0,9 × AvgCost),
|
||||||
|
bezogen auf alle geschlossenen Positionen. Braucht KEINE Preis-Historie —
|
||||||
|
Sell-Preis vs. AvgCost der Position reicht als v1-Näherung.
|
||||||
|
- Traits: `sells_at_loss` (Ratio > 0,15) ergänzt das vorhandene `holds_to_resolution`.
|
||||||
|
Ein Konsument bildet daraus selbst HOLDER (`holds_to_resolution` ∧ ¬`sells_at_loss`),
|
||||||
|
STOPLOSS, MIXED.
|
||||||
|
|
||||||
|
### E5. Kopierbarkeits-Aggregate (im Profil)
|
||||||
|
|
||||||
|
- `medianMarketVolumeUsd`: Median des `Market.Volume` (bzw. Volume24h) der vom Trader
|
||||||
|
gehandelten Märkte — handelt er in Kleinstmärkten, bewegt der Kopierer den Preis.
|
||||||
|
- `medianPostFillDriftPct`: Median-Preisänderung nach seinen Buys (aus vorhandenem
|
||||||
|
`TradeContext.PriceAfter1m`/`FollowerFillPrice60s`, nur enriched Trades; Anzahl
|
||||||
|
der Datenpunkte mit ausgeben).
|
||||||
|
- `netEdgeAfterFeesPct`: `AvgReturnPct` (E2, Fenster B) minus kategorie-gewichteter
|
||||||
|
Taker-Fee (aus `Market.FeeRateBps` — wird bereits erfasst!) minus konfigurierbarem
|
||||||
|
Spread-Aufschlag (`CopyCostSettings:SpreadPct`, Default 2,0). Generisch als
|
||||||
|
„Netto-Edge nach Kopierkosten" benannt.
|
||||||
|
|
||||||
|
### E6. Korrelations-/Portfolio-Sicht
|
||||||
|
|
||||||
|
- Neuer Endpoint `GET /api/traders/correlation?ids=1,2,3` (oder `?top=20`):
|
||||||
|
paarweise Jaccard-Ähnlichkeit über die gehandelten ConditionIds (Fenster B) +
|
||||||
|
Kategorie-Mix-Cosinus. Antwort: Matrix + je Paar die Overlap-Zahl.
|
||||||
|
- Kein „Portfolio-Empfehlungs"-Endpoint in v1 — die Auswahl-Logik (max. 2 je
|
||||||
|
Kategorie etc.) gehört zum Konsumenten. Wir liefern die Korrelationsdaten.
|
||||||
|
|
||||||
|
### E7. Martingale-Erkennung (M9) als Trait
|
||||||
|
|
||||||
|
`martingale_pattern`: mittleres Verhältnis Einsatz(nach verlorenem Markt) /
|
||||||
|
Einsatz(nach gewonnenem Markt) über die Sequenz der abgeschlossenen Märkte;
|
||||||
|
Trait ab Verhältnis ≥ 1,5 bei ≥ 20 Märkten. (Einsatz = investiertes Kapital je
|
||||||
|
Markt aus D2c.)
|
||||||
|
|
||||||
|
### E8. Abnahme Teil E
|
||||||
|
|
||||||
|
1. Bestehende Tests grün (Assertions unverändert) + neue Tests: Fenster-Metriken
|
||||||
|
(bekannte Markt-Menge, zwei Fenster → korrekte Werte je Fenster), Preisband-
|
||||||
|
Winrate (Band-Zuordnung + Ränder 0,895/0,90), `stop_loss_ratio`
|
||||||
|
(Positiv-/Negativfall), Jaccard-Berechnung, Martingale (steigende Einsätze nach
|
||||||
|
Losses → Trait; konstante → kein Trait).
|
||||||
|
2. `GET /api/traders/{id}/profile` liefert für einen analysierten Trader alle Blöcke
|
||||||
|
gefüllt; Antwortzeit < 200 ms (nur persistierte Daten).
|
||||||
|
3. `docs/API.md` um Profile-/Correlation-Endpoint ergänzt.
|
||||||
|
4. Kein PolyTrader-spezifisches Vokabular in API/DTOs.
|
||||||
|
|
||||||
|
Reihenfolge: E1+E3 zuerst (Profil mit vorhandenen + Fingerprint-Daten), dann E2
|
||||||
|
(Fenster), E4/E5/E7 (Kennzahlen), E6 zuletzt. Teil E setzt D1/D2/D2c voraus.
|
||||||
|
|||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
# Predictalytics REST API — Referenz
|
||||||
|
|
||||||
|
> Stand: 2026-07-11 · Interne API ohne Authentifizierung (nur lokal betreiben!).
|
||||||
|
> Interaktive Doku: **`/swagger`** auf dem laufenden Server (Standalone-API und
|
||||||
|
> WinForms-Embedded-Server registrieren identische Endpoints über
|
||||||
|
> `ApiConfiguration.MapPredictalyticsEndpoints()` — dort ist die einzige Stelle,
|
||||||
|
> an der neue Endpoints registriert werden dürfen).
|
||||||
|
|
||||||
|
Konventionen: JSON mit camelCase-Feldnamen · Zeiten in UTC (ISO 8601) ·
|
||||||
|
Prozente auf 0–100-Skala · Beträge in USD · `WinRate` ist marktbasiert
|
||||||
|
(gewonnene abgeschlossene Märkte / abgeschlossene Märkte, nicht pro Trade).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Traders
|
||||||
|
|
||||||
|
### `GET /api/traders`
|
||||||
|
Trader-Liste, sortiert nach CombinedScore, dann TotalPnl.
|
||||||
|
|
||||||
|
| Parameter | Typ | Default | Beschreibung |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `skip` / `take` | int | 0 / 50 | Paging |
|
||||||
|
| `platform` | string | All | `Polymarket`, `Limitless`, … |
|
||||||
|
| `highlyCopyable` | bool | false | nur `CopytradingScore ≥ 60` |
|
||||||
|
|
||||||
|
Antwort: Array von **TraderDto** — `id`, `platform`, `platformUserId` (Wallet),
|
||||||
|
`displayName`, `tier`, `strategy`, `combinedScore`, `copytradingScore` (kombinierter
|
||||||
|
Estimator-Score), `copytradingQualityScore` (LCB-Edge), `copytradingCopyabilityScore`
|
||||||
|
(Alpha-Decay/Sizing), `winRate`, `totalPnl`, `totalTrades`, `trades30d`, `pnL30d`,
|
||||||
|
`winRate30d`, `estimatedBankroll`, `isOnWatchlist`, `isSuspectedBot`, `lastPolledAt`.
|
||||||
|
|
||||||
|
*Geplant (FIXPLAN D2):* `trait`-Parameter (mehrfach = UND) und `traits`-Feld im DTO.
|
||||||
|
|
||||||
|
### `GET /api/traders/{id}`
|
||||||
|
**TraderDetailDto** — alles aus TraderDto plus: `notes`, `manualPriorityOverride`,
|
||||||
|
Zeitfenster `pnL7d`/`winRate7d`/`pnL24h`/`winRate24h`, `currentBalance`
|
||||||
|
(kumulierter Cashflow seit Tracking-Beginn), Teil-Scores `activityScore`/
|
||||||
|
`qualityScore`/`volumeScore`/`timingScore`, `rank`, `createdAt`,
|
||||||
|
`aiStrategySummary`, `recentTrades` (letzte 50, **TradeDto**),
|
||||||
|
`categoryPerformances` (je Kategorie: `category`, `totalVolume`, `totalPnL`,
|
||||||
|
`totalTrades` = abgeschlossene Märkte, `winningTrades`, `winRate` 0–1).
|
||||||
|
|
||||||
|
**TradeDto:** `id`, `traderId`, `traderName`, `platform`, `dbMarketId`, `marketId`
|
||||||
|
(ConditionId), `marketQuestion`, `outcome`, `side` (`Buy`/`Sell`/`Redeem`/`Split`/
|
||||||
|
`Merge`/…), `price`, `size`, `amount`, `executedAt`.
|
||||||
|
|
||||||
|
### `GET /api/traders/{id}/deep-dive`
|
||||||
|
On-the-fly-Analyse über die letzten 500 Trades (lädt bei Bedarf Preis-Historie nach —
|
||||||
|
langsamster Endpoint). Felder: `classifiedStrategy`, `isSuspectedBot`,
|
||||||
|
`avgHoldDurationHours`, `avgPositionSizeUsd`, `marketsTraded`, `hedgingFrequency`,
|
||||||
|
`timingAccuracy`, `entryQuality`, `exitQuality` (je 0–100, 50 = neutral),
|
||||||
|
`botIndicators` (string[]), `summary`, `recentTrades` (100).
|
||||||
|
|
||||||
|
### `GET /api/traders/{id}/positions`
|
||||||
|
Offene und realisierte Positionen. Je **TraderPositionDto**: `marketId`,
|
||||||
|
`marketName`, `category`, `outcomeToken`, `sharesHeld`, `avgCost`, `realizedPnl`,
|
||||||
|
`unrealizedPnl`, `currentPrice`, `lastTradeExecutedAt`.
|
||||||
|
|
||||||
|
### `POST /api/traders?platform=Polymarket&wallet=0x…`
|
||||||
|
Trader manuell anlegen (Import + Discovery-Flag). Antwort: Trader-Id.
|
||||||
|
|
||||||
|
### `POST /api/traders/{id}/priority?score=…`
|
||||||
|
Manuellen Prioritäts-Override setzen (ohne `score`: löschen).
|
||||||
|
|
||||||
|
### `POST /api/traders/{id}/watchlist` · `DELETE /api/traders/{id}/watchlist`
|
||||||
|
Watchlist-Zugehörigkeit setzen/entfernen. Watchlist-Trader sind von Retention/
|
||||||
|
Cleanup ausgenommen und werden bevorzugt synchronisiert/enriched.
|
||||||
|
|
||||||
|
### `POST /api/traders/{id}/ai-analysis?manual=true|false`
|
||||||
|
LLM-Strategie-Analyse anstoßen (synchron; `manual=true` nutzt das teurere Modell).
|
||||||
|
Antwort: `{ summary }`. Persistiert `aiStrategySummary`, `strategy`, `isSuspectedBot`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Markets
|
||||||
|
|
||||||
|
### `GET /api/markets`
|
||||||
|
Aktive Märkte. Parameter: `skip`/`take`, `platform`, `category`
|
||||||
|
(z. B. `Sports`, `Politics`, `Crypto`), `query` (Textsuche in Frage/ConditionId).
|
||||||
|
Je **MarketDto**: `id`, `platform`, `question`, `volume`, `liquidity`, `endDate`,
|
||||||
|
`isResolved`.
|
||||||
|
|
||||||
|
### `GET /api/markets/{id}`
|
||||||
|
**MarketDetailDto**: zusätzlich `platformMarketId` (ConditionId), `description`,
|
||||||
|
`category`, `subcategory`, `resolutionOutcome`, `imageUrl`, `botActivityScore`/
|
||||||
|
`uniqueTradersCount`/`averageTradeSize` (⚠ derzeit immer 0 — MarketAnalytics wird
|
||||||
|
noch nicht berechnet), `outcomes` (`name`, `price`), `recentTrades` (50).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Watchlist
|
||||||
|
|
||||||
|
### `GET /api/watchlist`
|
||||||
|
Alle Einträge: `traderId`, `displayName`, `platformUserId`, `platform`, `totalPnl`,
|
||||||
|
`winRate`, `copytradingScore`, `label`, `notes`, `createdAt`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Jobs (asynchrone Hintergrund-Aufträge)
|
||||||
|
|
||||||
|
Jobs werden von den Workern abgearbeitet (Worker müssen laufen!). Status:
|
||||||
|
`Pending` → `InProgress` → `Completed`/`Failed`.
|
||||||
|
|
||||||
|
| Endpoint | Wirkung |
|
||||||
|
|---|---|
|
||||||
|
| `GET /api/jobs?skip&take` | Liste, neueste zuerst: `id`, `jobType`, `status`, `traderId`, `traderName`, `createdAt`, `startedAt`, `completedAt`, `errorMessage` |
|
||||||
|
| `POST /api/jobs/sync/{traderId}` | Trade-Historie synchronisieren (HistorySync) |
|
||||||
|
| `POST /api/jobs/analyze/{traderId}` | PnL-Engine + Copytrading-Estimator (TraderAnalysis) |
|
||||||
|
| `POST /api/jobs/deep-resync/{traderId}` | Komplette Historie paginiert neu importieren; löscht vorher COMPACT-Aggregate und alle Positionen des Traders |
|
||||||
|
| `POST /api/jobs/deep-resync-pruned` | DeepResync-Jobs für alle Trader mit gepruneten Positionen/COMPACT-Trades enqueuen |
|
||||||
|
| `POST /api/jobs/analyze-backlog?take=50` | TraderAnalysis-Jobs für unanalysierte Trader enqueuen (nie analysiert zuerst, dann nach TotalTrades) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dashboard, Suche, Alerts, Sonstiges
|
||||||
|
|
||||||
|
| Endpoint | Beschreibung |
|
||||||
|
|---|---|
|
||||||
|
| `GET /api/dashboard` | Kennzahlen-Übersicht: `totalTraders`, `activeTraders24h`, `totalTrades`, `volume24h`, `unreadAlerts`, `watchlistCount`, `topTraders` (Top 5 nach PnL7d), `largestTrades` (24h), `recentAlerts`, `platformBreakdown` |
|
||||||
|
| `GET /api/search?q=…` | Sucht Trader (DisplayName **und** Wallet-Adresse) und Märkte; Antwort `{ traders, markets }` |
|
||||||
|
| `GET /api/alerts?count&unreadOnly` | Alert-Liste |
|
||||||
|
| `PUT /api/alerts/{id}/read` | Alert als gelesen markieren |
|
||||||
|
| `GET /api/health` | `{ status, timestamp }` |
|
||||||
|
|
||||||
|
## Dev (Diagnose)
|
||||||
|
|
||||||
|
| Endpoint | Beschreibung |
|
||||||
|
|---|---|
|
||||||
|
| `GET /api/dev/verify-positions/{traderId}` | Vergleicht unsere `TraderPositions` mit Polymarkets `/positions`-API; Antwort: `mismatches[]`, `match`, `dbPositionsCount`, `apiPositionsCount` |
|
||||||
|
|
||||||
|
> Ein früherer `POST /api/dev/repair-db` wurde entfernt (fehlerhaftes SQL,
|
||||||
|
> destruktiv). Reparatur läuft über den WinForms-Menüpunkt **Development →
|
||||||
|
> Recalculate All Traders**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Geplante Erweiterungen (FIXPLAN Teil D/E — für API-Konsumenten relevant)
|
||||||
|
|
||||||
|
- `GET /api/traits` + `trait`-Filter auf `/api/traders`; `traits`-Liste in den Trader-DTOs (D2).
|
||||||
|
- Markt-Rendite-Metriken im Detail-DTO: `medianWinReturnPct`, `avgWinReturnPct`,
|
||||||
|
`medianLossReturnPct`, `avgLossReturnPct`, `profitFactor` (D2c).
|
||||||
|
- `GET /api/traders/{id}/profile` — vollständiges Analyse-Profil eines Traders in
|
||||||
|
einem Aufruf (Kennzahlen + Zeitfenster + Traits + Fingerprint-Verteilungen +
|
||||||
|
Kopierbarkeits-Aggregate), gedacht für externe Konsumenten wie PolyTrader (Teil E).
|
||||||
|
- Out-of-Sample-Fenster (A/B), Preisband-Profil, Stop-Loss-Klassifikation,
|
||||||
|
Portfolio-/Korrelations-Sicht (Teil E).
|
||||||
@@ -13,7 +13,7 @@ public static class DevEndpoints
|
|||||||
{
|
{
|
||||||
public static void MapDevEndpoints(this IEndpointRouteBuilder routes)
|
public static void MapDevEndpoints(this IEndpointRouteBuilder routes)
|
||||||
{
|
{
|
||||||
var group = routes.MapGroup("/api/dev");
|
var group = routes.MapGroup("/api/dev").WithTags("Dev");
|
||||||
|
|
||||||
group.MapGet("/verify-positions/{traderId:int}", async (int traderId, AppDbContext db, IEnumerable<IPlatformProvider> providers, CancellationToken ct) =>
|
group.MapGet("/verify-positions/{traderId:int}", async (int traderId, AppDbContext db, IEnumerable<IPlatformProvider> providers, CancellationToken ct) =>
|
||||||
{
|
{
|
||||||
@@ -68,30 +68,11 @@ public static class DevEndpoints
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group.MapPost("/repair-db", async (AppDbContext db, CancellationToken ct) =>
|
// NOTE: a former POST /repair-db endpoint was removed here on purpose:
|
||||||
{
|
// its raw SQL referenced non-existent columns/tables (Trades.Type/Payout,
|
||||||
await db.Database.ExecuteSqlRawAsync(@"
|
// Traders.LastPositionsUpdatedAt, table "Jobs") and would have deleted ALL
|
||||||
-- 1. Fake PnL Trades löschen
|
// TraderPositions including the pruned-history conserves. The supported
|
||||||
DELETE FROM Trades WHERE Type = 4 AND Payout = 0 AND Size = 0;
|
// repair path is the "Recalculate All Traders" action in the WinForms
|
||||||
|
// Development menu (EmbeddedWebServer.RunRecalculateAllTradersAsync).
|
||||||
-- 2. Positionen löschen, da sie durch Sync neu aufgebaut werden
|
|
||||||
DELETE FROM TraderPositions;
|
|
||||||
|
|
||||||
-- 3. Sync State von Tradern zurücksetzen (DeepResync forcieren)
|
|
||||||
UPDATE Traders SET
|
|
||||||
IsInitialImportComplete = 0,
|
|
||||||
LastTradesUpdatedAt = NULL,
|
|
||||||
LastPositionsUpdatedAt = NULL,
|
|
||||||
TotalPnl = 0,
|
|
||||||
WinRate = 0,
|
|
||||||
TotalTrades = 0,
|
|
||||||
EstimatedBankroll = 0;
|
|
||||||
|
|
||||||
-- 4. Jobs abbrechen
|
|
||||||
UPDATE Jobs SET Status = 5 WHERE Status IN (1, 2);
|
|
||||||
", ct);
|
|
||||||
|
|
||||||
return Results.Ok("DB repaired. DeepResync needed.");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public static class JobEndpoints
|
|||||||
{
|
{
|
||||||
public static void MapJobEndpoints(this IEndpointRouteBuilder routes)
|
public static void MapJobEndpoints(this IEndpointRouteBuilder routes)
|
||||||
{
|
{
|
||||||
var group = routes.MapGroup("/api/jobs");
|
var group = routes.MapGroup("/api/jobs").WithTags("Jobs");
|
||||||
|
|
||||||
group.MapGet("/", async (IJobRepository repo, int skip = 0, int take = 50, CancellationToken ct = default) =>
|
group.MapGet("/", async (IJobRepository repo, int skip = 0, int take = 50, CancellationToken ct = default) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,7 +41,12 @@ public record TraderDetailDto(
|
|||||||
int Trades30d,
|
int Trades30d,
|
||||||
decimal PnL30d,
|
decimal PnL30d,
|
||||||
decimal WinRate30d,
|
decimal WinRate30d,
|
||||||
|
decimal PnL7d,
|
||||||
|
decimal WinRate7d,
|
||||||
|
decimal PnL24h,
|
||||||
|
decimal WinRate24h,
|
||||||
decimal EstimatedBankroll,
|
decimal EstimatedBankroll,
|
||||||
|
decimal CurrentBalance,
|
||||||
decimal ActivityScore,
|
decimal ActivityScore,
|
||||||
decimal QualityScore,
|
decimal QualityScore,
|
||||||
decimal VolumeScore,
|
decimal VolumeScore,
|
||||||
|
|||||||
@@ -236,7 +236,9 @@ public class AnalyticsService : IAnalyticsService
|
|||||||
return new TraderDetailDto(trader.Id, trader.Platform.ToString(), trader.PlatformUserId, trader.DisplayName,
|
return new TraderDetailDto(trader.Id, trader.Platform.ToString(), trader.PlatformUserId, trader.DisplayName,
|
||||||
trader.Notes, trader.Tier.ToString(), trader.Strategy.ToString(), trader.IsSuspectedBot, trader.ManualPriorityOverride,
|
trader.Notes, trader.Tier.ToString(), trader.Strategy.ToString(), trader.IsSuspectedBot, trader.ManualPriorityOverride,
|
||||||
trader.WinRate, trader.TotalPnl, trader.TotalTrades,
|
trader.WinRate, trader.TotalPnl, trader.TotalTrades,
|
||||||
a?.Trades30d ?? 0, a?.PnL30d ?? 0, a?.WinRate30d ?? 0, a?.EstimatedBankroll ?? 0,
|
a?.Trades30d ?? 0, a?.PnL30d ?? 0, a?.WinRate30d ?? 0,
|
||||||
|
a?.PnL7d ?? 0, a?.WinRate7d ?? 0, a?.PnL24h ?? 0, a?.WinRate24h ?? 0,
|
||||||
|
a?.EstimatedBankroll ?? 0, a?.CurrentBalance ?? 0,
|
||||||
s?.ActivityScore ?? 0, s?.QualityScore ?? 0, s?.VolumeScore ?? 0, s?.TimingScore ?? 0,
|
s?.ActivityScore ?? 0, s?.QualityScore ?? 0, s?.VolumeScore ?? 0, s?.TimingScore ?? 0,
|
||||||
s?.CombinedScore ?? 0, a?.CopytradingScore ?? 0, a?.CopytradingQualityScore ?? 0, a?.CopytradingCopyabilityScore ?? 0, s?.Rank ?? 0, wl != null, trader.CreatedAt, trader.LastPolledAt,
|
s?.CombinedScore ?? 0, a?.CopytradingScore ?? 0, a?.CopytradingQualityScore ?? 0, a?.CopytradingCopyabilityScore ?? 0, s?.Rank ?? 0, wl != null, trader.CreatedAt, trader.LastPolledAt,
|
||||||
trader.AiStrategySummary,
|
trader.AiStrategySummary,
|
||||||
|
|||||||
Reference in New Issue
Block a user