using System.ComponentModel.DataAnnotations; using Domain.Entities.Stocks.ValueObject; namespace Domain.Entities.Stocks; /// /// 채권(국채전문유통/일반채권/소액채권) 일별매매 시세 (KRX OpenAPI bon 엔드포인트) — (BondMarket, Code, TradeDate) UNIQUE. /// 세 시장을 한 테이블에 담고 BondMarket 으로 구분한다. 채권은 가격 + 수익률(YD) 쌍으로 제공되며 주식/증권상품과 필드가 다르다: /// - 가격/전일대비: CLSPRC/OPNPRC/HGPRC/LWPRC + CMPPREVDD_PRC (주식의 TDD_* 아님). 등락률(FLUC_RT)·시가총액·상장주식수는 미제공. /// - 수익률(YD): CLSPRC_YD(만기수익률=종가수익률), OPNPRC_YD/HGPRC_YD/LWPRC_YD — 모두 nullable(미체결 시 ""). /// - 국채전문(KtsGovt)만: MaturityYears(BND_EXP_TP_NM 만기년수), IssueType(GOVBND_ISU_TP_NM 종목구분 예: "지표"/"경과"). 일반/소액은 null. /// 단축코드(ISU_CD)는 12자리 ISIN(예: KR103503GE96) — 6자리 정규화 없이 원문 유지. 가격은 decimal(18,2), 수익률은 decimal(9,3)%. /// public class BondDailyTrade { [Key] public long ID { get; private set; } /// 채권 시장 구분 (1=국채전문유통, 2=일반채권, 3=소액채권) public BondMarket BondMarket { get; private set; } /// 단축코드 (ISU_CD) — 12자리 ISIN(예: KR103503GE96) public string Code { get; private set; } = default!; /// 종목명 (ISU_NM) public string Name { get; private set; } = default!; /// 거래일 (BAS_DD) public DateOnly TradeDate { get; private set; } /// 종가 가격 (CLSPRC) public decimal Close { get; private set; } /// 시가 가격 (OPNPRC) public decimal Open { get; private set; } /// 고가 가격 (HGPRC) public decimal High { get; private set; } /// 저가 가격 (LWPRC) public decimal Low { get; private set; } /// 전일 대비 (CMPPREVDD_PRC) — 미제공("") 시 0 public decimal ChangeAmount { get; private set; } /// 만기수익률 % (CLSPRC_YD 종가수익률) — 미체결 시 null public decimal? YieldToMaturity { get; private set; } /// 시가 수익률 % (OPNPRC_YD) — 미체결 시 null public decimal? OpenYield { get; private set; } /// 고가 수익률 % (HGPRC_YD) — 미체결 시 null public decimal? HighYield { get; private set; } /// 저가 수익률 % (LWPRC_YD) — 미체결 시 null public decimal? LowYield { get; private set; } /// 거래량 (ACC_TRDVOL) public long Volume { get; private set; } /// 거래대금 (ACC_TRDVAL, 원) public long TradeValue { get; private set; } /// 만기년수 (BND_EXP_TP_NM) — 국채전문만. 일반/소액은 null public string? MaturityYears { get; private set; } /// 종목구분 (GOVBND_ISU_TP_NM 예: "지표"/"경과") — 국채전문만. 일반/소액은 null public string? IssueType { get; private set; } public DateTime CreatedAt { get; private set; } = DateTime.UtcNow; private BondDailyTrade() { } public static BondDailyTrade Create( BondMarket bondMarket, string code, string name, DateOnly tradeDate, decimal close, decimal open, decimal high, decimal low, decimal changeAmount, decimal? yieldToMaturity, decimal? openYield, decimal? highYield, decimal? lowYield, long volume, long tradeValue, string? maturityYears = null, string? issueType = null ) { if (!Enum.IsDefined(bondMarket)) { throw new ArgumentOutOfRangeException(nameof(bondMarket)); } if (string.IsNullOrWhiteSpace(code)) { throw new ArgumentException("code required", nameof(code)); } if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("name required", nameof(name)); } return new BondDailyTrade { BondMarket = bondMarket, Code = code.Trim(), Name = name.Trim(), TradeDate = tradeDate, Close = close, Open = open, High = high, Low = low, ChangeAmount = changeAmount, YieldToMaturity = yieldToMaturity, OpenYield = openYield, HighYield = highYield, LowYield = lowYield, Volume = volume, TradeValue = tradeValue, MaturityYears = maturityYears, IssueType = issueType }; } /// 동일 (BondMarket, Code, TradeDate) 재수집 시 값 갱신 (upsert) public void Update( string name, decimal close, decimal open, decimal high, decimal low, decimal changeAmount, decimal? yieldToMaturity, decimal? openYield, decimal? highYield, decimal? lowYield, long volume, long tradeValue, string? maturityYears = null, string? issueType = null ) { Name = name.Trim(); Close = close; Open = open; High = high; Low = low; ChangeAmount = changeAmount; YieldToMaturity = yieldToMaturity; OpenYield = openYield; HighYield = highYield; LowYield = lowYield; Volume = volume; TradeValue = tradeValue; MaturityYears = maturityYears; IssueType = issueType; } }