using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using PolyTrader.Modules.ResolutionFarming.Models; using PolyTrader.Modules.ResolutionFarming.Persistence; using PolyTraderSharp; using PolyTraderSharp.Services; namespace PolyTrader.Web.Endpoints { public static class ResolutionFarmingEndpoints { public static IEndpointRouteBuilder MapResolutionFarmingEndpoints(this IEndpointRouteBuilder routes) { var group = routes.MapGroup("/api/modules/resolutionfarming"); // 1. Kandidaten (Scanner-Ergebnisse) group.MapGet("/candidates", (IServiceProvider sp, TradingState state, int? accountId) => { int accId = accountId.GetValueOrDefault(-1); if (accId <= 0) { var firstAcc = state.Accounts.Values.OrderBy(a => a.AccountId).FirstOrDefault(); if (firstAcc != null) accId = firstAcc.AccountId; } var repo = sp.GetService(); List candidates; try { candidates = repo?.GetRecent(accId, 500) ?? new List(); } catch { candidates = new List(); } var list = candidates.Select(c => new { id = c.Id, scannedAt = c.ScannedAt.ToString("yyyy-MM-dd HH:mm:ss"), accountId = c.AccountId, tokenId = c.TokenId, marketSlug = c.MarketSlug, marketQuestion = c.MarketQuestion, outcome = c.Outcome, category = c.Category, clusterKey = c.ClusterKey, ask = c.Ask, feeBps = c.FeeBps, netEdgePct = c.NetEdgePct, score = c.Score, endDate = c.EndDate.HasValue ? c.EndDate.Value.ToString("yyyy-MM-dd HH:mm") : "—", accepted = c.Accepted, rejectReason = c.RejectReason }).ToList(); return Results.Ok(new { accountId = accId, count = list.Count, candidates = list }); }); // 2. Offene Positionen group.MapGet("/positions", (IServiceProvider sp, TradingState state, int? accountId) => { int accId = accountId.GetValueOrDefault(-1); var repo = sp.GetService(); List positions; try { if (accId > 0) positions = repo?.GetOpen(accId) ?? new List(); else positions = repo?.GetAllOpen() ?? new List(); } catch { positions = new List(); } var ordered = positions.OrderByDescending(p => p.OpenedAt).ToList(); decimal totalAmount = ordered.Sum(p => p.AmountUsd); var list = ordered.Select(p => { string accName = $"#{p.AccountId}"; if (state.Accounts.TryGetValue(p.AccountId, out var acc) && !string.IsNullOrEmpty(acc.Name)) accName = acc.Name + (p.IsDemo ? " (Demo)" : ""); return new { accountId = p.AccountId, account = accName, isDemo = p.IsDemo, tokenId = p.TokenId, marketSlug = p.MarketSlug, marketQuestion = p.MarketQuestion, outcome = p.Outcome, category = p.Category, clusterKey = p.ClusterKey, entryPrice = p.EntryPrice, size = p.Size, amountUsd = p.AmountUsd, entryFeeBps = p.EntryFeeBps, openedAt = p.OpenedAt.ToString("yyyy-MM-dd HH:mm"), endDate = p.EndDate.HasValue ? p.EndDate.Value.ToString("yyyy-MM-dd HH:mm") : "—", status = p.Status }; }).ToList(); return Results.Ok(new { accountId = accId, count = list.Count, totalAmount, positions = list }); }); // 3. Historie (abgeschlossene Trades) group.MapGet("/history", (IServiceProvider sp, TradingState state, int? accountId, string? search) => { int accId = accountId.GetValueOrDefault(-1); var repo = sp.GetService(); List allClosed; try { allClosed = repo?.Find(t => accId <= 0 || t.AccountId == accId) ?? new List(); } catch { allClosed = new List(); } var query = allClosed.AsEnumerable(); if (!string.IsNullOrWhiteSpace(search)) { string q = search.Trim(); query = query.Where(t => (t.MarketQuestion != null && t.MarketQuestion.Contains(q, StringComparison.OrdinalIgnoreCase)) || (t.Outcome != null && t.Outcome.Contains(q, StringComparison.OrdinalIgnoreCase)) || (t.Category != null && t.Category.Contains(q, StringComparison.OrdinalIgnoreCase))); } var filtered = query.OrderByDescending(t => t.ClosedAt).ToList(); decimal totalPnl = filtered.Sum(t => t.RealizedPnl); int wins = filtered.Count(t => t.RealizedPnl > 0); decimal winrate = filtered.Count > 0 ? 100m * wins / filtered.Count : 0m; var list = filtered.Select(t => { string accName = $"#{t.AccountId}"; if (state.Accounts.TryGetValue(t.AccountId, out var acc) && !string.IsNullOrEmpty(acc.Name)) accName = acc.Name + (t.IsDemo ? " (Demo)" : ""); return new { id = t.Id, accountId = t.AccountId, account = accName, isDemo = t.IsDemo, tokenId = t.TokenId, marketSlug = t.MarketSlug, marketQuestion = t.MarketQuestion, outcome = t.Outcome, category = t.Category, clusterKey = t.ClusterKey, entryPrice = t.EntryPrice, exitPrice = t.ExitPrice, size = t.Size, realizedPnl = t.RealizedPnl, pnlPercent = t.PnlPercent, totalFees = t.TotalFees, openedAt = t.OpenedAt.ToString("yyyy-MM-dd HH:mm"), closedAt = t.ClosedAt.ToString("yyyy-MM-dd HH:mm"), exitReason = t.ExitReason, redeemStatus = t.RedeemStatus }; }).ToList(); return Results.Ok(new { accountId = accId, summary = new { count = list.Count, pnl = totalPnl, wins, winRate = winrate }, trades = list }); }); // 4. Einstellungen abrufen group.MapGet("/settings/{accountId:int}", (IServiceProvider sp, int accountId) => { var repo = sp.GetService(); RfSettings? settings = null; try { settings = repo?.Get(accountId); } catch { } if (settings == null) settings = new RfSettings { AccountId = accountId }; return Results.Ok(settings); }); // 5. Einstellungen speichern group.MapPost("/settings/{accountId:int}", (IServiceProvider sp, TerminalLogger logger, int accountId, RfSettings input) => { input.AccountId = accountId; var repo = sp.GetService(); var failed = PersistenceGuard.Write(repo != null, () => repo!.Upsert(input), logger, $"ResolutionFarming-Einstellungen fuer Konto {accountId}"); if (failed != null) return failed; return Results.Ok(new { success = true, settings = input }); }); return routes; } } }