| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- namespace Domain.Entities.Paper;
- /// <summary>
- /// 모의투자 보유 포지션 — (AccountID, StockCode) UNIQUE. StockCode 는 FK 없이 denorm 보관 (d0 §2.1).
- /// ReservedQuantity 로 이중 매도를 차단한다.
- /// </summary>
- public class PaperPosition
- {
- [ForeignKey(nameof(AccountID))]
- public virtual PaperAccount Account { get; private set; } = null!;
- [Key]
- public int ID { get; private set; }
- public int AccountID { get; private set; }
- /// <summary>단축코드 (char 6)</summary>
- public string StockCode { get; private set; } = default!;
- /// <summary>보유 수량</summary>
- public int Quantity { get; private set; }
- /// <summary>매도 주문으로 예약된 수량</summary>
- public int ReservedQuantity { get; private set; }
- /// <summary>평균 단가 (18,4) — 매수 체결 시 평균단가법 갱신</summary>
- public decimal AvgPrice { get; private set; }
- public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow;
- [Timestamp]
- public byte[] RowVersion { get; private set; } = default!;
- private PaperPosition() { }
- public static PaperPosition Create(int accountID, string stockCode, int quantity, decimal avgPrice)
- {
- if (accountID <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(accountID));
- }
- if (stockCode is not { Length: 6 })
- {
- throw new ArgumentException("stockCode must be 6 chars", nameof(stockCode));
- }
- if (quantity < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(quantity));
- }
- return new PaperPosition
- {
- AccountID = accountID,
- StockCode = stockCode,
- Quantity = quantity,
- ReservedQuantity = 0,
- AvgPrice = avgPrice
- };
- }
- /// <summary>매도 주문 접수 시 수량 예약 — 가용 수량(Quantity - ReservedQuantity) 부족 시 실패.</summary>
- public void ReserveSell(int quantity)
- {
- if (quantity <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(quantity));
- }
- if (Quantity - ReservedQuantity < quantity)
- {
- throw new InvalidOperationException("매도 가능한 수량이 부족합니다.");
- }
- ReservedQuantity += quantity;
- UpdatedAt = DateTime.UtcNow;
- }
- /// <summary>매도 예약 환원 (주문 취소·거부).</summary>
- public void ReleaseSellReserve(int quantity)
- {
- if (quantity <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(quantity));
- }
- if (ReservedQuantity < quantity)
- {
- throw new InvalidOperationException("환원할 예약 수량이 부족합니다.");
- }
- ReservedQuantity -= quantity;
- UpdatedAt = DateTime.UtcNow;
- }
- }
|