| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- using System.ComponentModel.DataAnnotations;
- using Domain.Entities.Stocks.ValueObject;
- namespace Domain.Entities.Stocks;
- /// <summary>
- /// 증권상품(ETF/ETN/ELW) 일별매매 시세 (KRX OpenAPI etp 엔드포인트) — (EtpType, Code, TradeDate) UNIQUE.
- /// 세 상품을 한 테이블에 담고 EtpType 으로 구분한다. 공통 컬럼(가격/거래/시총) + 유형별 nullable 컬럼:
- /// - ETF/ETN 공통: Nav(ETF NAV / ETN 지표가치 IV), NetAssetTotal(순자산총액 / 지표가치총액), BaseIndexName·BaseIndexClose(기초지수명·종가)
- /// - ELW: Underlying·UnderlyingClose(기초자산명·종가)
- /// 등락률(ChangeRate)은 KrxStockParser 와 동일하게 소수 % 그대로 저장(Bp 아님). ELW 는 자체 등락률 필드가 없어 0.
- /// 가격은 decimal(18,2) — ETF NAV·기초지수 종가에 소수가 있어 int 대신 decimal 로 통일.
- /// </summary>
- public class EtpDailyTrade
- {
- [Key]
- public long ID { get; private set; }
- /// <summary>증권상품 구분 (1=ETF, 2=ETN, 3=ELW)</summary>
- public EtpType EtpType { get; private set; }
- /// <summary>단축코드 (ISU_CD) — ETF/ETN 은 6자리 숫자, ELW 는 영숫자 6자리(예: 58K001)</summary>
- public string Code { get; private set; } = default!;
- /// <summary>종목명 (ISU_NM)</summary>
- public string Name { get; private set; } = default!;
- /// <summary>거래일 (BAS_DD)</summary>
- public DateOnly TradeDate { get; private set; }
- /// <summary>종가 (TDD_CLSPRC)</summary>
- public decimal Close { get; private set; }
- /// <summary>시가 (TDD_OPNPRC)</summary>
- public decimal Open { get; private set; }
- /// <summary>고가 (TDD_HGPRC)</summary>
- public decimal High { get; private set; }
- /// <summary>저가 (TDD_LWPRC)</summary>
- public decimal Low { get; private set; }
- /// <summary>전일 대비 (CMPPREVDD_PRC)</summary>
- public decimal ChangeAmount { get; private set; }
- /// <summary>등락률 % (FLUC_RT) — ELW 는 자체 등락률이 없어 0</summary>
- public decimal ChangeRate { get; private set; }
- /// <summary>거래량 (ACC_TRDVOL)</summary>
- public long Volume { get; private set; }
- /// <summary>거래대금 (ACC_TRDVAL, 원)</summary>
- public long TradeValue { get; private set; }
- /// <summary>시가총액 (MKTCAP, 원) — 미제공 시 null</summary>
- public long? MarketCap { get; private set; }
- /// <summary>상장증권수/상장좌수 (LIST_SHRS) — 미제공 시 null</summary>
- public long? ListedShares { get; private set; }
- /// <summary>ETF NAV / ETN 지표가치(IV, PER1SECU_INDIC_VAL). ELW 는 null</summary>
- public decimal? Nav { get; private set; }
- /// <summary>ETF 순자산총액(INVSTASST_NETASST_TOTAMT) / ETN 지표가치총액(INDIC_VAL_AMT), 원. ELW 는 null</summary>
- public long? NetAssetTotal { get; private set; }
- /// <summary>기초지수명 (IDX_IND_NM) — ETF/ETN. ELW 는 null</summary>
- public string? BaseIndexName { get; private set; }
- /// <summary>기초지수 종가 (OBJ_STKPRC_IDX) — ETF/ETN. 미제공("")·ELW 는 null</summary>
- public decimal? BaseIndexClose { get; private set; }
- /// <summary>ELW 기초자산명 (ULY_NM). ETF/ETN 은 null</summary>
- public string? Underlying { get; private set; }
- /// <summary>ELW 기초자산 종가 (ULY_PRC). ETF/ETN 은 null</summary>
- public decimal? UnderlyingClose { get; private set; }
- public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
- private EtpDailyTrade() { }
- public static EtpDailyTrade Create(
- EtpType etpType,
- string code,
- string name,
- DateOnly tradeDate,
- decimal close,
- decimal open,
- decimal high,
- decimal low,
- decimal changeAmount,
- decimal changeRate,
- long volume,
- long tradeValue,
- long? marketCap = null,
- long? listedShares = null,
- decimal? nav = null,
- long? netAssetTotal = null,
- string? baseIndexName = null,
- decimal? baseIndexClose = null,
- string? underlying = null,
- decimal? underlyingClose = null
- ) {
- if (!Enum.IsDefined(etpType))
- {
- throw new ArgumentOutOfRangeException(nameof(etpType));
- }
- if (string.IsNullOrWhiteSpace(code))
- {
- throw new ArgumentException("code required", nameof(code));
- }
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentException("name required", nameof(name));
- }
- return new EtpDailyTrade
- {
- EtpType = etpType,
- Code = code.Trim(),
- Name = name.Trim(),
- TradeDate = tradeDate,
- Close = close,
- Open = open,
- High = high,
- Low = low,
- ChangeAmount = changeAmount,
- ChangeRate = changeRate,
- Volume = volume,
- TradeValue = tradeValue,
- MarketCap = marketCap,
- ListedShares = listedShares,
- Nav = nav,
- NetAssetTotal = netAssetTotal,
- BaseIndexName = baseIndexName,
- BaseIndexClose = baseIndexClose,
- Underlying = underlying,
- UnderlyingClose = underlyingClose
- };
- }
- /// <summary>동일 (EtpType, Code, TradeDate) 재수집 시 값 갱신 (upsert)</summary>
- public void Update(
- string name,
- decimal close,
- decimal open,
- decimal high,
- decimal low,
- decimal changeAmount,
- decimal changeRate,
- long volume,
- long tradeValue,
- long? marketCap = null,
- long? listedShares = null,
- decimal? nav = null,
- long? netAssetTotal = null,
- string? baseIndexName = null,
- decimal? baseIndexClose = null,
- string? underlying = null,
- decimal? underlyingClose = null
- ) {
- Name = name.Trim();
- Close = close;
- Open = open;
- High = high;
- Low = low;
- ChangeAmount = changeAmount;
- ChangeRate = changeRate;
- Volume = volume;
- TradeValue = tradeValue;
- MarketCap = marketCap;
- ListedShares = listedShares;
- Nav = nav;
- NetAssetTotal = netAssetTotal;
- BaseIndexName = baseIndexName;
- BaseIndexClose = baseIndexClose;
- Underlying = underlying;
- UnderlyingClose = underlyingClose;
- }
- }
|