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).
|
||||
|
||||
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)
|
||||
{
|
||||
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) =>
|
||||
{
|
||||
@@ -68,30 +68,11 @@ public static class DevEndpoints
|
||||
});
|
||||
});
|
||||
|
||||
group.MapPost("/repair-db", async (AppDbContext db, CancellationToken ct) =>
|
||||
{
|
||||
await db.Database.ExecuteSqlRawAsync(@"
|
||||
-- 1. Fake PnL Trades löschen
|
||||
DELETE FROM Trades WHERE Type = 4 AND Payout = 0 AND Size = 0;
|
||||
|
||||
-- 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.");
|
||||
});
|
||||
// NOTE: a former POST /repair-db endpoint was removed here on purpose:
|
||||
// its raw SQL referenced non-existent columns/tables (Trades.Type/Payout,
|
||||
// Traders.LastPositionsUpdatedAt, table "Jobs") and would have deleted ALL
|
||||
// TraderPositions including the pruned-history conserves. The supported
|
||||
// repair path is the "Recalculate All Traders" action in the WinForms
|
||||
// Development menu (EmbeddedWebServer.RunRecalculateAllTradersAsync).
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public static class JobEndpoints
|
||||
{
|
||||
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) =>
|
||||
{
|
||||
|
||||
@@ -41,7 +41,12 @@ public record TraderDetailDto(
|
||||
int Trades30d,
|
||||
decimal PnL30d,
|
||||
decimal WinRate30d,
|
||||
decimal PnL7d,
|
||||
decimal WinRate7d,
|
||||
decimal PnL24h,
|
||||
decimal WinRate24h,
|
||||
decimal EstimatedBankroll,
|
||||
decimal CurrentBalance,
|
||||
decimal ActivityScore,
|
||||
decimal QualityScore,
|
||||
decimal VolumeScore,
|
||||
|
||||
@@ -236,7 +236,9 @@ public class AnalyticsService : IAnalyticsService
|
||||
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.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?.CombinedScore ?? 0, a?.CopytradingScore ?? 0, a?.CopytradingQualityScore ?? 0, a?.CopytradingCopyabilityScore ?? 0, s?.Rank ?? 0, wl != null, trader.CreatedAt, trader.LastPolledAt,
|
||||
trader.AiStrategySummary,
|
||||
|
||||
Reference in New Issue
Block a user