using System.ComponentModel.DataAnnotations; namespace Domain.Entities.Stocks; /// /// 채권지수 일별시세 (KRX OpenAPI idx/bon_dd_trd) — (GroupName, TradeDate) UNIQUE. /// 응답 shape 이 주식/파생 지수(OHLC + 거래량)와 완전히 달라 IndexDailyPrice 로 재사용 불가하여 별도 테이블로 둔다: /// 채권지수는 종가/시가/고가/저가 대신 4종 지수(총수익·순가격·제로재투자·Call재투자·시장가격) + 채권 고유지표(듀레이션·컨벡시티·평균만기수익률 YTM)를 제공한다. /// 지수 그룹(BND_IDX_GRP_NM, 예: "KRX 채권지수", "KTB 지수", "국고채프라임지수")별 1행. 결측치("" 또는 "-")는 null 로 저장한다. /// 지수값/전일대비는 소수 그대로(decimal(18,4)), 듀레이션·컨벡시티·YTM 도 decimal(18,4) 로 보관. /// public class BondIndexDailyPrice { [Key] public long ID { get; private set; } /// 채권지수 그룹명 (BND_IDX_GRP_NM, 예: "KRX 채권지수") public string GroupName { get; private set; } = default!; /// 거래일 (BAS_DD) public DateOnly TradeDate { get; private set; } /// 총수익지수 (TOT_EARNG_IDX) — 미제공 시 null public decimal? TotalEarningIndex { get; private set; } /// 총수익지수 전일 대비 (TOT_EARNG_IDX_CMPPREVDD) public decimal? TotalEarningChange { get; private set; } /// 순가격지수 (NETPRC_IDX) public decimal? NetPriceIndex { get; private set; } /// 순가격지수 전일 대비 (NETPRC_IDX_CMPPREVDD) public decimal? NetPriceChange { get; private set; } /// 제로재투자지수 (ZERO_REINVST_IDX) public decimal? ZeroReinvestIndex { get; private set; } /// 제로재투자지수 전일 대비 (ZERO_REINVST_IDX_CMPPREVDD) public decimal? ZeroReinvestChange { get; private set; } /// Call재투자지수 (CALL_REINVST_IDX) public decimal? CallReinvestIndex { get; private set; } /// Call재투자지수 전일 대비 (CALL_REINVST_IDX_CMPPREVDD) public decimal? CallReinvestChange { get; private set; } /// 시장가격지수 (MKT_PRC_IDX) public decimal? MarketPriceIndex { get; private set; } /// 시장가격지수 전일 대비 (MKT_PRC_IDX_CMPPREVDD) public decimal? MarketPriceChange { get; private set; } /// 평균 듀레이션 (AVG_DURATION) public decimal? AvgDuration { get; private set; } /// 평균 컨벡시티 (AVG_CONVEXITY_PRC) public decimal? AvgConvexity { get; private set; } /// 평균 만기수익률 YTM (BND_IDX_AVG_YD) public decimal? AvgYield { get; private set; } public DateTime CreatedAt { get; private set; } = DateTime.UtcNow; private BondIndexDailyPrice() { } public static BondIndexDailyPrice Create( string groupName, DateOnly tradeDate, decimal? totalEarningIndex, decimal? totalEarningChange, decimal? netPriceIndex, decimal? netPriceChange, decimal? zeroReinvestIndex, decimal? zeroReinvestChange, decimal? callReinvestIndex, decimal? callReinvestChange, decimal? marketPriceIndex, decimal? marketPriceChange, decimal? avgDuration, decimal? avgConvexity, decimal? avgYield ) { if (string.IsNullOrWhiteSpace(groupName)) { throw new ArgumentException("groupName required", nameof(groupName)); } return new BondIndexDailyPrice { GroupName = groupName.Trim(), TradeDate = tradeDate, TotalEarningIndex = totalEarningIndex, TotalEarningChange = totalEarningChange, NetPriceIndex = netPriceIndex, NetPriceChange = netPriceChange, ZeroReinvestIndex = zeroReinvestIndex, ZeroReinvestChange = zeroReinvestChange, CallReinvestIndex = callReinvestIndex, CallReinvestChange = callReinvestChange, MarketPriceIndex = marketPriceIndex, MarketPriceChange = marketPriceChange, AvgDuration = avgDuration, AvgConvexity = avgConvexity, AvgYield = avgYield }; } /// 동일 (GroupName, TradeDate) 재수집 시 값 갱신 (upsert) public void Update( decimal? totalEarningIndex, decimal? totalEarningChange, decimal? netPriceIndex, decimal? netPriceChange, decimal? zeroReinvestIndex, decimal? zeroReinvestChange, decimal? callReinvestIndex, decimal? callReinvestChange, decimal? marketPriceIndex, decimal? marketPriceChange, decimal? avgDuration, decimal? avgConvexity, decimal? avgYield ) { TotalEarningIndex = totalEarningIndex; TotalEarningChange = totalEarningChange; NetPriceIndex = netPriceIndex; NetPriceChange = netPriceChange; ZeroReinvestIndex = zeroReinvestIndex; ZeroReinvestChange = zeroReinvestChange; CallReinvestIndex = callReinvestIndex; CallReinvestChange = callReinvestChange; MarketPriceIndex = marketPriceIndex; MarketPriceChange = marketPriceChange; AvgDuration = avgDuration; AvgConvexity = avgConvexity; AvgYield = avgYield; } }