Handler.cs 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.Messaging;
  3. using Application.Helpers;
  4. using Microsoft.EntityFrameworkCore;
  5. namespace Application.Features.Api.Paper.GetPositions;
  6. internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
  7. {
  8. public async Task<Response> Handle(Query request, CancellationToken ct)
  9. {
  10. var accountID = await db.PaperAccount.AsNoTracking().Where(c => c.MemberID == request.MemberID).Select(c => (int?)c.ID).FirstOrDefaultAsync(ct);
  11. if (accountID is null)
  12. {
  13. return new Response([]);
  14. }
  15. var positions = await db.PaperPosition.AsNoTracking()
  16. .Where(c => c.AccountID == accountID.Value && c.Quantity > 0)
  17. .Select(c => new { c.StockCode, c.Quantity, c.ReservedQuantity, c.AvgPrice })
  18. .ToListAsync(ct);
  19. if (positions.Count == 0)
  20. {
  21. return new Response([]);
  22. }
  23. var codes = positions.Select(c => c.StockCode).Distinct().ToList();
  24. var latestCloses = await PaperValuation.GetLatestClosesAsync(db, codes, ct);
  25. var names = await db.Stock.AsNoTracking()
  26. .Where(c => codes.Contains(c.Code))
  27. .Select(c => new { c.Code, c.Name })
  28. .ToDictionaryAsync(c => c.Code, c => c.Name, ct);
  29. var rows = new List<Response.Row>();
  30. foreach (var p in positions)
  31. {
  32. decimal? latestClose = latestCloses.TryGetValue(p.StockCode, out var close) ? close : null;
  33. var marketValue = (latestClose ?? 0m) * p.Quantity;
  34. var costBasis = p.AvgPrice * p.Quantity;
  35. var unrealizedPnL = marketValue - costBasis;
  36. var unrealizedBp = costBasis > 0 ? (int)Math.Round(unrealizedPnL / costBasis * 10000m, MidpointRounding.AwayFromZero) : 0;
  37. rows.Add(new Response.Row(
  38. p.StockCode,
  39. names.GetValueOrDefault(p.StockCode, p.StockCode),
  40. p.Quantity,
  41. p.ReservedQuantity,
  42. p.AvgPrice,
  43. latestClose,
  44. marketValue,
  45. unrealizedPnL,
  46. unrealizedBp));
  47. }
  48. return new Response(rows);
  49. }
  50. }