Handler.cs 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Microsoft.EntityFrameworkCore;
  4. namespace Application.Features.Api.Paper.GetTrackRecord;
  5. /// <summary>
  6. /// 프로필 모의투자 성적 (익명 허용) — SID→회원→계좌→최신 스냅샷. 계좌/스냅샷 없으면 HasAccount=false (d4 M3 리더보드 백엔드 구현).
  7. /// 승률은 전체 매도 기준(RealizedPnL>0 / RealizedPnL!=null).
  8. /// </summary>
  9. internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
  10. {
  11. private static readonly Response Empty = new(false, 0, 0, 0, 0, null);
  12. public async Task<Response> Handle(Query request, CancellationToken ct)
  13. {
  14. if (string.IsNullOrWhiteSpace(request.Sid))
  15. {
  16. return Empty;
  17. }
  18. var memberID = await db.Member.AsNoTracking().Where(c => c.SID == request.Sid).Select(c => (int?)c.ID).FirstOrDefaultAsync(ct);
  19. if (memberID is null)
  20. {
  21. return Empty;
  22. }
  23. var accountID = await db.PaperAccount.AsNoTracking().Where(c => c.MemberID == memberID.Value).Select(c => (int?)c.ID).FirstOrDefaultAsync(ct);
  24. if (accountID is null)
  25. {
  26. return Empty;
  27. }
  28. var latest = await db.PaperDailySnapshot.AsNoTracking()
  29. .Where(c => c.AccountID == accountID.Value)
  30. .OrderByDescending(c => c.TradeDate)
  31. .Select(c => new { c.CumReturnBp, c.MddBp, c.FillCountCum, c.TradeDate })
  32. .FirstOrDefaultAsync(ct);
  33. if (latest is null)
  34. {
  35. return Empty;
  36. }
  37. var totalSells = await db.PaperFill.AsNoTracking().CountAsync(c => c.RealizedPnL != null && c.Order.AccountID == accountID.Value, ct);
  38. var winningSells = await db.PaperFill.AsNoTracking().CountAsync(c => c.RealizedPnL > 0 && c.Order.AccountID == accountID.Value, ct);
  39. var winRateBp = totalSells > 0 ? (int)Math.Round(winningSells / (double)totalSells * 10000, MidpointRounding.AwayFromZero) : 0;
  40. return new Response(true, latest.CumReturnBp, latest.MddBp, winRateBp, latest.FillCountCum, latest.TradeDate);
  41. }
  42. }