| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 |
- using Application.Abstractions.Data;
- using Application.Abstractions.Messaging;
- using Application.Helpers;
- using Microsoft.EntityFrameworkCore;
- namespace Application.Features.Api.Paper.GetPositions;
- internal sealed class Handler(IAppDbContext db) : IQueryHandler<Query, Response>
- {
- public async Task<Response> Handle(Query request, CancellationToken ct)
- {
- var accountID = await db.PaperAccount.AsNoTracking().Where(c => c.MemberID == request.MemberID).Select(c => (int?)c.ID).FirstOrDefaultAsync(ct);
- if (accountID is null)
- {
- return new Response([]);
- }
- var positions = await db.PaperPosition.AsNoTracking()
- .Where(c => c.AccountID == accountID.Value && c.Quantity > 0)
- .Select(c => new { c.StockCode, c.Quantity, c.ReservedQuantity, c.AvgPrice })
- .ToListAsync(ct);
- if (positions.Count == 0)
- {
- return new Response([]);
- }
- var codes = positions.Select(c => c.StockCode).Distinct().ToList();
- var latestCloses = await PaperValuation.GetLatestClosesAsync(db, codes, ct);
- var names = await db.Stock.AsNoTracking()
- .Where(c => codes.Contains(c.Code))
- .Select(c => new { c.Code, c.Name })
- .ToDictionaryAsync(c => c.Code, c => c.Name, ct);
- var rows = new List<Response.Row>();
- foreach (var p in positions)
- {
- decimal? latestClose = latestCloses.TryGetValue(p.StockCode, out var close) ? close : null;
- var marketValue = (latestClose ?? 0m) * p.Quantity;
- var costBasis = p.AvgPrice * p.Quantity;
- var unrealizedPnL = marketValue - costBasis;
- var unrealizedBp = costBasis > 0 ? (int)Math.Round(unrealizedPnL / costBasis * 10000m, MidpointRounding.AwayFromZero) : 0;
- rows.Add(new Response.Row(
- p.StockCode,
- names.GetValueOrDefault(p.StockCode, p.StockCode),
- p.Quantity,
- p.ReservedQuantity,
- p.AvgPrice,
- latestClose,
- marketValue,
- unrealizedPnL,
- unrealizedBp));
- }
- return new Response(rows);
- }
- }
|