| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- using Domain.Entities.Paper.ValueObject;
- namespace Domain.Entities.Paper;
- /// <summary>
- /// 모의투자 주문 — 접수 시 서버가 FillRule/TargetDate/CancelableUntil 을 확정한다 (d4 §③ 체결 규칙).
- /// 실제 기표는 TargetDate 시세가 도착하는 배치(M2)에서 수행 (Pending → Filled/Rejected).
- /// </summary>
- public class PaperOrder
- {
- [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!;
- public PaperOrderSide Side { get; private set; }
- public PaperFillRule FillRule { get; private set; }
- public int Quantity { get; private set; }
- /// <summary>매수 예약금 (매도는 0)</summary>
- public decimal ReservedAmount { get; private set; }
- /// <summary>체결 기준일</summary>
- public DateOnly TargetDate { get; private set; }
- /// <summary>취소 가능 시한 — 이후 취소 불가 (look-ahead 어뷰징 차단)</summary>
- public DateTime CancelableUntil { get; private set; }
- public PaperOrderStatus Status { get; private set; }
- /// <summary>거부 사유 (Rejected 시)</summary>
- public string? RejectReason { get; private set; }
- public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
- private PaperOrder() { }
- public static PaperOrder Create(
- int accountID,
- string stockCode,
- PaperOrderSide side,
- PaperFillRule fillRule,
- int quantity,
- decimal reservedAmount,
- DateOnly targetDate,
- DateTime cancelableUntil
- ) {
- 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));
- }
- if (reservedAmount < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(reservedAmount));
- }
- return new PaperOrder
- {
- AccountID = accountID,
- StockCode = stockCode,
- Side = side,
- FillRule = fillRule,
- Quantity = quantity,
- ReservedAmount = reservedAmount,
- TargetDate = targetDate,
- CancelableUntil = cancelableUntil,
- Status = PaperOrderStatus.Pending
- };
- }
- /// <summary>취소 — Pending 만 취소 가능. 성공 시 true. 시한 검증은 호출자.</summary>
- public bool Cancel()
- {
- if (Status != PaperOrderStatus.Pending)
- {
- return false;
- }
- Status = PaperOrderStatus.Cancelled;
- return true;
- }
- /// <summary>체결 확정 (배치 M2).</summary>
- public void MarkFilled()
- {
- Status = PaperOrderStatus.Filled;
- }
- /// <summary>거부 처리 (배치 M2).</summary>
- public void MarkRejected(string reason)
- {
- Status = PaperOrderStatus.Rejected;
- RejectReason = reason;
- }
- }
|