EtpDailyTrade.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. using System.ComponentModel.DataAnnotations;
  2. using Domain.Entities.Stocks.ValueObject;
  3. namespace Domain.Entities.Stocks;
  4. /// <summary>
  5. /// 증권상품(ETF/ETN/ELW) 일별매매 시세 (KRX OpenAPI etp 엔드포인트) — (EtpType, Code, TradeDate) UNIQUE.
  6. /// 세 상품을 한 테이블에 담고 EtpType 으로 구분한다. 공통 컬럼(가격/거래/시총) + 유형별 nullable 컬럼:
  7. /// - ETF/ETN 공통: Nav(ETF NAV / ETN 지표가치 IV), NetAssetTotal(순자산총액 / 지표가치총액), BaseIndexName·BaseIndexClose(기초지수명·종가)
  8. /// - ELW: Underlying·UnderlyingClose(기초자산명·종가)
  9. /// 등락률(ChangeRate)은 KrxStockParser 와 동일하게 소수 % 그대로 저장(Bp 아님). ELW 는 자체 등락률 필드가 없어 0.
  10. /// 가격은 decimal(18,2) — ETF NAV·기초지수 종가에 소수가 있어 int 대신 decimal 로 통일.
  11. /// </summary>
  12. public class EtpDailyTrade
  13. {
  14. [Key]
  15. public long ID { get; private set; }
  16. /// <summary>증권상품 구분 (1=ETF, 2=ETN, 3=ELW)</summary>
  17. public EtpType EtpType { get; private set; }
  18. /// <summary>단축코드 (ISU_CD) — ETF/ETN 은 6자리 숫자, ELW 는 영숫자 6자리(예: 58K001)</summary>
  19. public string Code { get; private set; } = default!;
  20. /// <summary>종목명 (ISU_NM)</summary>
  21. public string Name { get; private set; } = default!;
  22. /// <summary>거래일 (BAS_DD)</summary>
  23. public DateOnly TradeDate { get; private set; }
  24. /// <summary>종가 (TDD_CLSPRC)</summary>
  25. public decimal Close { get; private set; }
  26. /// <summary>시가 (TDD_OPNPRC)</summary>
  27. public decimal Open { get; private set; }
  28. /// <summary>고가 (TDD_HGPRC)</summary>
  29. public decimal High { get; private set; }
  30. /// <summary>저가 (TDD_LWPRC)</summary>
  31. public decimal Low { get; private set; }
  32. /// <summary>전일 대비 (CMPPREVDD_PRC)</summary>
  33. public decimal ChangeAmount { get; private set; }
  34. /// <summary>등락률 % (FLUC_RT) — ELW 는 자체 등락률이 없어 0</summary>
  35. public decimal ChangeRate { get; private set; }
  36. /// <summary>거래량 (ACC_TRDVOL)</summary>
  37. public long Volume { get; private set; }
  38. /// <summary>거래대금 (ACC_TRDVAL, 원)</summary>
  39. public long TradeValue { get; private set; }
  40. /// <summary>시가총액 (MKTCAP, 원) — 미제공 시 null</summary>
  41. public long? MarketCap { get; private set; }
  42. /// <summary>상장증권수/상장좌수 (LIST_SHRS) — 미제공 시 null</summary>
  43. public long? ListedShares { get; private set; }
  44. /// <summary>ETF NAV / ETN 지표가치(IV, PER1SECU_INDIC_VAL). ELW 는 null</summary>
  45. public decimal? Nav { get; private set; }
  46. /// <summary>ETF 순자산총액(INVSTASST_NETASST_TOTAMT) / ETN 지표가치총액(INDIC_VAL_AMT), 원. ELW 는 null</summary>
  47. public long? NetAssetTotal { get; private set; }
  48. /// <summary>기초지수명 (IDX_IND_NM) — ETF/ETN. ELW 는 null</summary>
  49. public string? BaseIndexName { get; private set; }
  50. /// <summary>기초지수 종가 (OBJ_STKPRC_IDX) — ETF/ETN. 미제공("")·ELW 는 null</summary>
  51. public decimal? BaseIndexClose { get; private set; }
  52. /// <summary>ELW 기초자산명 (ULY_NM). ETF/ETN 은 null</summary>
  53. public string? Underlying { get; private set; }
  54. /// <summary>ELW 기초자산 종가 (ULY_PRC). ETF/ETN 은 null</summary>
  55. public decimal? UnderlyingClose { get; private set; }
  56. public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
  57. private EtpDailyTrade() { }
  58. public static EtpDailyTrade Create(
  59. EtpType etpType,
  60. string code,
  61. string name,
  62. DateOnly tradeDate,
  63. decimal close,
  64. decimal open,
  65. decimal high,
  66. decimal low,
  67. decimal changeAmount,
  68. decimal changeRate,
  69. long volume,
  70. long tradeValue,
  71. long? marketCap = null,
  72. long? listedShares = null,
  73. decimal? nav = null,
  74. long? netAssetTotal = null,
  75. string? baseIndexName = null,
  76. decimal? baseIndexClose = null,
  77. string? underlying = null,
  78. decimal? underlyingClose = null
  79. ) {
  80. if (!Enum.IsDefined(etpType))
  81. {
  82. throw new ArgumentOutOfRangeException(nameof(etpType));
  83. }
  84. if (string.IsNullOrWhiteSpace(code))
  85. {
  86. throw new ArgumentException("code required", nameof(code));
  87. }
  88. if (string.IsNullOrWhiteSpace(name))
  89. {
  90. throw new ArgumentException("name required", nameof(name));
  91. }
  92. return new EtpDailyTrade
  93. {
  94. EtpType = etpType,
  95. Code = code.Trim(),
  96. Name = name.Trim(),
  97. TradeDate = tradeDate,
  98. Close = close,
  99. Open = open,
  100. High = high,
  101. Low = low,
  102. ChangeAmount = changeAmount,
  103. ChangeRate = changeRate,
  104. Volume = volume,
  105. TradeValue = tradeValue,
  106. MarketCap = marketCap,
  107. ListedShares = listedShares,
  108. Nav = nav,
  109. NetAssetTotal = netAssetTotal,
  110. BaseIndexName = baseIndexName,
  111. BaseIndexClose = baseIndexClose,
  112. Underlying = underlying,
  113. UnderlyingClose = underlyingClose
  114. };
  115. }
  116. /// <summary>동일 (EtpType, Code, TradeDate) 재수집 시 값 갱신 (upsert)</summary>
  117. public void Update(
  118. string name,
  119. decimal close,
  120. decimal open,
  121. decimal high,
  122. decimal low,
  123. decimal changeAmount,
  124. decimal changeRate,
  125. long volume,
  126. long tradeValue,
  127. long? marketCap = null,
  128. long? listedShares = null,
  129. decimal? nav = null,
  130. long? netAssetTotal = null,
  131. string? baseIndexName = null,
  132. decimal? baseIndexClose = null,
  133. string? underlying = null,
  134. decimal? underlyingClose = null
  135. ) {
  136. Name = name.Trim();
  137. Close = close;
  138. Open = open;
  139. High = high;
  140. Low = low;
  141. ChangeAmount = changeAmount;
  142. ChangeRate = changeRate;
  143. Volume = volume;
  144. TradeValue = tradeValue;
  145. MarketCap = marketCap;
  146. ListedShares = listedShares;
  147. Nav = nav;
  148. NetAssetTotal = netAssetTotal;
  149. BaseIndexName = baseIndexName;
  150. BaseIndexClose = baseIndexClose;
  151. Underlying = underlying;
  152. UnderlyingClose = underlyingClose;
  153. }
  154. }