using System.ComponentModel.DataAnnotations; namespace Domain.Entities.Stocks; /// /// SEIBro 채권 조기상환 정보 (getBondOptionXrcInfo) — Call/Put 옵션 행사 조기상환 (Wave 4). 자연키 UNIQUE (Isin, ErlyRedDt). /// 조기상환일(ERLY_RED_DT) 날짜 스윕으로 수집한다. 한 채권이 여러 조기상환일을 가질 수 있어 (Isin, ErlyRedDt) 로 구분. /// public class BondEarlyRedemption { [Key] public int ID { get; private set; } /// 종목코드 (ISIN) public string Isin { get; private set; } = default!; /// 한글종목명 (KOR_SECN_NM) public string? KorSecnNm { get; private set; } /// 행사시작일 (XRC_BEGIN_DT) public DateOnly? XrcBeginDt { get; private set; } /// 행사종료일 (XRC_EXPRY_DT) public DateOnly? XrcExpryDt { get; private set; } /// 조기상환일 (ERLY_RED_DT) public DateOnly ErlyRedDt { get; private set; } /// 옵션구분코드 (OPTION_TPCD) — 9401CALL 9402PUT 9403CALL+PUT public string? OptionTpcd { get; private set; } /// 적용이자율 (APLI_IRATE) public decimal? ApliIrate { get; private set; } /// 조기상환금액 (ERLY_REDAMT_VAL) public decimal? ErlyRedamtVal { get; private set; } /// 이자지급금액 (INT_PAY_AMT) public decimal? IntPayAmt { get; private set; } /// 최근발행잔액 (ISSU_REMA) public decimal? IssuRema { get; private set; } /// 행사비율 (XRC_RATIO) public decimal? XrcRatio { get; private set; } public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow; private BondEarlyRedemption() { } public static BondEarlyRedemption Create( string isin, string? korSecnNm, DateOnly? xrcBeginDt, DateOnly? xrcExpryDt, DateOnly erlyRedDt, string? optionTpcd, decimal? apliIrate, decimal? erlyRedamtVal, decimal? intPayAmt, decimal? issuRema, decimal? xrcRatio) { if (string.IsNullOrWhiteSpace(isin)) { throw new ArgumentException("isin required", nameof(isin)); } return new BondEarlyRedemption { Isin = isin.Trim(), KorSecnNm = Trimmed(korSecnNm), XrcBeginDt = xrcBeginDt, XrcExpryDt = xrcExpryDt, ErlyRedDt = erlyRedDt, OptionTpcd = Trimmed(optionTpcd), ApliIrate = apliIrate, ErlyRedamtVal = erlyRedamtVal, IntPayAmt = intPayAmt, IssuRema = issuRema, XrcRatio = xrcRatio }; } /// 재수집 반영 — 변경 필드가 있을 때만 UpdatedAt 갱신 (자연키 Isin/ErlyRedDt 는 불변) public void Update( string? korSecnNm, DateOnly? xrcBeginDt, DateOnly? xrcExpryDt, string? optionTpcd, decimal? apliIrate, decimal? erlyRedamtVal, decimal? intPayAmt, decimal? issuRema, decimal? xrcRatio) { var changed = false; changed |= Set(Trimmed(korSecnNm), KorSecnNm, v => KorSecnNm = v); changed |= Set(xrcBeginDt, XrcBeginDt, v => XrcBeginDt = v); changed |= Set(xrcExpryDt, XrcExpryDt, v => XrcExpryDt = v); changed |= Set(Trimmed(optionTpcd), OptionTpcd, v => OptionTpcd = v); changed |= Set(apliIrate, ApliIrate, v => ApliIrate = v); changed |= Set(erlyRedamtVal, ErlyRedamtVal, v => ErlyRedamtVal = v); changed |= Set(intPayAmt, IntPayAmt, v => IntPayAmt = v); changed |= Set(issuRema, IssuRema, v => IssuRema = v); changed |= Set(xrcRatio, XrcRatio, v => XrcRatio = v); if (changed) { UpdatedAt = DateTime.UtcNow; } } private static string? Trimmed(string? value) { return string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } private static bool Set(T incoming, T current, Action assign) { if (EqualityComparer.Default.Equals(incoming, current)) { return false; } assign(incoming); return true; } }