using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Domain.Entities.Paper; /// /// 모의투자 체결 결과 — 주문당 1건(OrderID UNIQUE)으로 재실행 멱등을 보장한다 (d4 §③). /// 배치(M2)가 TargetDate 시세로 기표한다. 매도 시 RealizedPnL(실현손익)을 기록. /// public class PaperFill { [ForeignKey(nameof(OrderID))] public virtual PaperOrder Order { get; private set; } = null!; [Key] public int ID { get; private set; } public int OrderID { get; private set; } /// 체결가 (18,0) public decimal Price { get; private set; } public int Quantity { get; private set; } /// 수수료 (토큰 sink) public decimal Fee { get; private set; } /// 거래세 (매도 시, 토큰 sink) public decimal Tax { get; private set; } /// 체결 총액 (Price × Quantity ± 수수료/세금 반영은 핸들러/배치 정책) public decimal Amount { get; private set; } /// 매도 확정 손익 (매수는 null) public decimal? RealizedPnL { get; private set; } /// 체결가 기준일 public DateOnly PriceDate { get; private set; } public DateTime FilledAt { get; private set; } = DateTime.UtcNow; private PaperFill() { } public static PaperFill Create( int orderID, decimal price, int quantity, decimal fee, decimal tax, decimal amount, DateOnly priceDate, decimal? realizedPnL = null ) { if (orderID <= 0) { throw new ArgumentOutOfRangeException(nameof(orderID)); } if (quantity <= 0) { throw new ArgumentOutOfRangeException(nameof(quantity)); } return new PaperFill { OrderID = orderID, Price = price, Quantity = quantity, Fee = fee, Tax = tax, Amount = amount, PriceDate = priceDate, RealizedPnL = realizedPnL }; } /// /// 신규 주문(ID 미확정) 케이스 — Order 내비게이션으로 생성해 EF 가 저장 시 FK(OrderID)를 자동 채우게 한다. /// 상폐 강제청산 등 시스템 주문+체결을 한 번에 저장할 때 사용 (d4 §⑨). /// public static PaperFill CreateFor( PaperOrder order, decimal price, int quantity, decimal fee, decimal tax, decimal amount, DateOnly priceDate, decimal? realizedPnL = null ) { ArgumentNullException.ThrowIfNull(order); if (quantity <= 0) { throw new ArgumentOutOfRangeException(nameof(quantity)); } return new PaperFill { Order = order, Price = price, Quantity = quantity, Fee = fee, Tax = tax, Amount = amount, PriceDate = priceDate, RealizedPnL = realizedPnL }; } }