BondMaster.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using System.ComponentModel.DataAnnotations;
  2. namespace Domain.Entities.Stocks;
  3. /// <summary>
  4. /// SEIBro 채권 종목 정보 (getBondStatInfo) — 만기·표면이자율·옵션·보증·순위·신용등급 (Wave 4, ★P2). PK Isin.
  5. /// **신용등급 4사(KIS/NICE/SCI/KR) 유일 소스.** 신용등급 코드는 코드표가 커서 원문 문자열로 저장(채권.md 공통표 참조).
  6. /// rolling per-ISIN 대상 = BondIssuance.Isin ∪ 기존 KRX BondDailyTrade.Code(ISIN). GetBondDetail 의 주 조인 대상.
  7. /// 발행당시 등급이라 재수집 시 스냅샷 갱신(변경 시 UpdatedAt). 소수 precision 은 넉넉히(채권.md NUMBER(13~14,10)).
  8. /// </summary>
  9. public class BondMaster
  10. {
  11. /// <summary>종목코드 (ISIN) — PK</summary>
  12. [Key]
  13. [MaxLength(12)]
  14. public string Isin { get; private set; } = default!;
  15. /// <summary>발행사 번호 (ISSUCO_CUSTNO)</summary>
  16. public int? IssucoCustno { get; private set; }
  17. /// <summary>한글종목명 (KOR_SECN_NM)</summary>
  18. public string? KorSecnNm { get; private set; }
  19. /// <summary>종목종류코드 (SECN_KACD) — 1108일반회사채 등</summary>
  20. public string? SecnKacd { get; private set; }
  21. /// <summary>발행일자 (ISSU_DT)</summary>
  22. public DateOnly? IssuDt { get; private set; }
  23. /// <summary>만기일자 (XPIR_DT)</summary>
  24. public DateOnly? XpirDt { get; private set; }
  25. /// <summary>발행통화코드 (ISSU_CUR_CD) — KRW</summary>
  26. public string? IssuCurCd { get; private set; }
  27. /// <summary>최초발행금액 (FIRST_ISSU_AMT)</summary>
  28. public decimal? FirstIssuAmt { get; private set; }
  29. /// <summary>발행잔액 (ISSU_REMA)</summary>
  30. public decimal? IssuRema { get; private set; }
  31. /// <summary>납입금액 (PAYIN_AMT)</summary>
  32. public decimal? PayinAmt { get; private set; }
  33. /// <summary>표면이자율 (COUPON_RATE)</summary>
  34. public decimal? CouponRate { get; private set; }
  35. /// <summary>만기상환율 (XPIRED_RATE)</summary>
  36. public decimal? XpiredRate { get; private set; }
  37. /// <summary>모집방법코드 (RECU_WHCD) — 11공모 12사모 13일반 14CBO기초사모 21매출</summary>
  38. public string? RecuWhcd { get; private set; }
  39. /// <summary>발행방법코드 (ISSU_WHCD) — 1실물 2전액등록 …</summary>
  40. public string? IssuWhcd { get; private set; }
  41. /// <summary>특이채권종류구분코드 (PARTICUL_BOND_KIND_TPCD) — 1전환 2교환 3신주인수권 4분리형신주인수권 6이익참가 9해당없음</summary>
  42. public string? ParticulBondKindTpcd { get; private set; }
  43. /// <summary>옵션구분코드 (OPTION_TPCD) — 9401CALL 9402PUT 9403CALL+PUT 9404NOTE 0000해당없음</summary>
  44. public string? OptionTpcd { get; private set; }
  45. /// <summary>강제조기상환여부 (FORC_ERLY_RED_YN, Y/N)</summary>
  46. public bool? ForcErlyRedYn { get; private set; }
  47. /// <summary>금리변동구분코드 (MR_CHG_TPCD) — 1고정 2변동 3고정+변동</summary>
  48. public string? MrChgTpcd { get; private set; }
  49. /// <summary>등록기관구분코드 (REGI_ORG_TPCD) — 1:KSD</summary>
  50. public string? RegiOrgTpcd { get; private set; }
  51. /// <summary>보증구분코드 (GRTY_TPCD) — 1보증 2무보증 3담보부 4일반</summary>
  52. public string? GrtyTpcd { get; private set; }
  53. /// <summary>기명구분코드 (SIGNA_TPCD) — 1기명 2무기명 3기명+무기명</summary>
  54. public string? SignaTpcd { get; private set; }
  55. /// <summary>순위구분코드 (RANK_TPCD) — 1선순위 2후순위 3중순위 9해당없음</summary>
  56. public string? RankTpcd { get; private set; }
  57. /// <summary>이자지급방법구분코드 (INT_PAY_WAY_TPCD) — 1이표 2할인 3복리 4단리</summary>
  58. public string? IntPayWayTpcd { get; private set; }
  59. /// <summary>단리복리구분코드 (SINT_CINT_TPCD) — 1단리 2복리 3단리+복리</summary>
  60. public string? SintCintTpcd { get; private set; }
  61. /// <summary>이자율변동구분코드 (IRATE_CHG_TPCD) — 1이율동일 2이율상이 3비정형</summary>
  62. public string? IrateChgTpcd { get; private set; }
  63. /// <summary>만기보장수익율 (XPIR_GUAR_PRATE)</summary>
  64. public decimal? XpirGuarPrate { get; private set; }
  65. /// <summary>만기보장수익율구분코드 (XPIR_GUAR_PRATE_TPCD) — 01연복리 02 3개월복리 …</summary>
  66. public string? XpirGuarPrateTpcd { get; private set; }
  67. /// <summary>원금상환방법코드 (PRCP_RED_WHCD) — 11만기 21중도 31조기 …</summary>
  68. public string? PrcpRedWhcd { get; private set; }
  69. /// <summary>상장일 (APLI_DT)</summary>
  70. public DateOnly? ApliDt { get; private set; }
  71. /// <summary>상장폐지일자 (DLIST_DT)</summary>
  72. public DateOnly? DlistDt { get; private set; }
  73. /// <summary>KIS평가등급코드 (KIS_VALAT_GRD_CD, 발행당시) — 신용등급코드표 원문</summary>
  74. public string? KisValatGrdCd { get; private set; }
  75. /// <summary>NICE평가등급코드 (NICE_VALAT_GRD_CD) — 신용등급코드표 원문</summary>
  76. public string? NiceValatGrdCd { get; private set; }
  77. /// <summary>서울신용평가등급코드 (SCI_VALAT_GRD_CD) — 신용등급코드표 원문</summary>
  78. public string? SciValatGrdCd { get; private set; }
  79. /// <summary>한국기업평가등급코드 (KR_VALAT_GRD_CD) — 신용등급코드표 원문</summary>
  80. public string? KrValatGrdCd { get; private set; }
  81. /// <summary>전자증권여부 (ELTSC_YN) — Y전자 N예탁</summary>
  82. public bool? EltscYn { get; private set; }
  83. /// <summary>권리행사주체 (EXER_MBODY_TPCD) — 1KSD 2자체</summary>
  84. public string? ExerMbodyTpcd { get; private set; }
  85. /// <summary>이자산정방식 (INT_ESTM_MANN_TPCD) — 1정형 2비정형 3해당없음</summary>
  86. public string? IntEstmMannTpcd { get; private set; }
  87. public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow;
  88. private BondMaster() { }
  89. public static BondMaster Create(string isin, BondMasterFields f)
  90. {
  91. if (string.IsNullOrWhiteSpace(isin))
  92. {
  93. throw new ArgumentException("isin required", nameof(isin));
  94. }
  95. var entity = new BondMaster { Isin = isin.Trim() };
  96. entity.Apply(f);
  97. return entity;
  98. }
  99. /// <summary>재수집 반영 (PK Isin 은 불변) — 스냅샷 갱신, 변경 시에만 UpdatedAt</summary>
  100. public void Update(BondMasterFields f)
  101. {
  102. Apply(f);
  103. }
  104. private void Apply(BondMasterFields f)
  105. {
  106. var changed = false;
  107. changed |= Set(f.IssucoCustno, IssucoCustno, v => IssucoCustno = v);
  108. changed |= Set(Trimmed(f.KorSecnNm), KorSecnNm, v => KorSecnNm = v);
  109. changed |= Set(Trimmed(f.SecnKacd), SecnKacd, v => SecnKacd = v);
  110. changed |= Set(f.IssuDt, IssuDt, v => IssuDt = v);
  111. changed |= Set(f.XpirDt, XpirDt, v => XpirDt = v);
  112. changed |= Set(Trimmed(f.IssuCurCd), IssuCurCd, v => IssuCurCd = v);
  113. changed |= Set(f.FirstIssuAmt, FirstIssuAmt, v => FirstIssuAmt = v);
  114. changed |= Set(f.IssuRema, IssuRema, v => IssuRema = v);
  115. changed |= Set(f.PayinAmt, PayinAmt, v => PayinAmt = v);
  116. changed |= Set(f.CouponRate, CouponRate, v => CouponRate = v);
  117. changed |= Set(f.XpiredRate, XpiredRate, v => XpiredRate = v);
  118. changed |= Set(Trimmed(f.RecuWhcd), RecuWhcd, v => RecuWhcd = v);
  119. changed |= Set(Trimmed(f.IssuWhcd), IssuWhcd, v => IssuWhcd = v);
  120. changed |= Set(Trimmed(f.ParticulBondKindTpcd), ParticulBondKindTpcd, v => ParticulBondKindTpcd = v);
  121. changed |= Set(Trimmed(f.OptionTpcd), OptionTpcd, v => OptionTpcd = v);
  122. changed |= Set(f.ForcErlyRedYn, ForcErlyRedYn, v => ForcErlyRedYn = v);
  123. changed |= Set(Trimmed(f.MrChgTpcd), MrChgTpcd, v => MrChgTpcd = v);
  124. changed |= Set(Trimmed(f.RegiOrgTpcd), RegiOrgTpcd, v => RegiOrgTpcd = v);
  125. changed |= Set(Trimmed(f.GrtyTpcd), GrtyTpcd, v => GrtyTpcd = v);
  126. changed |= Set(Trimmed(f.SignaTpcd), SignaTpcd, v => SignaTpcd = v);
  127. changed |= Set(Trimmed(f.RankTpcd), RankTpcd, v => RankTpcd = v);
  128. changed |= Set(Trimmed(f.IntPayWayTpcd), IntPayWayTpcd, v => IntPayWayTpcd = v);
  129. changed |= Set(Trimmed(f.SintCintTpcd), SintCintTpcd, v => SintCintTpcd = v);
  130. changed |= Set(Trimmed(f.IrateChgTpcd), IrateChgTpcd, v => IrateChgTpcd = v);
  131. changed |= Set(f.XpirGuarPrate, XpirGuarPrate, v => XpirGuarPrate = v);
  132. changed |= Set(Trimmed(f.XpirGuarPrateTpcd), XpirGuarPrateTpcd, v => XpirGuarPrateTpcd = v);
  133. changed |= Set(Trimmed(f.PrcpRedWhcd), PrcpRedWhcd, v => PrcpRedWhcd = v);
  134. changed |= Set(f.ApliDt, ApliDt, v => ApliDt = v);
  135. changed |= Set(f.DlistDt, DlistDt, v => DlistDt = v);
  136. changed |= Set(Trimmed(f.KisValatGrdCd), KisValatGrdCd, v => KisValatGrdCd = v);
  137. changed |= Set(Trimmed(f.NiceValatGrdCd), NiceValatGrdCd, v => NiceValatGrdCd = v);
  138. changed |= Set(Trimmed(f.SciValatGrdCd), SciValatGrdCd, v => SciValatGrdCd = v);
  139. changed |= Set(Trimmed(f.KrValatGrdCd), KrValatGrdCd, v => KrValatGrdCd = v);
  140. changed |= Set(f.EltscYn, EltscYn, v => EltscYn = v);
  141. changed |= Set(Trimmed(f.ExerMbodyTpcd), ExerMbodyTpcd, v => ExerMbodyTpcd = v);
  142. changed |= Set(Trimmed(f.IntEstmMannTpcd), IntEstmMannTpcd, v => IntEstmMannTpcd = v);
  143. if (changed)
  144. {
  145. UpdatedAt = DateTime.UtcNow;
  146. }
  147. }
  148. private static string? Trimmed(string? value)
  149. {
  150. return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
  151. }
  152. private static bool Set<T>(T incoming, T current, Action<T> assign)
  153. {
  154. if (EqualityComparer<T>.Default.Equals(incoming, current))
  155. {
  156. return false;
  157. }
  158. assign(incoming);
  159. return true;
  160. }
  161. }
  162. /// <summary>BondMaster 필드 묶음 (getBondStatInfo 필드 다수 — 생성/갱신 인자 축약)</summary>
  163. public sealed record BondMasterFields(
  164. int? IssucoCustno,
  165. string? KorSecnNm,
  166. string? SecnKacd,
  167. DateOnly? IssuDt,
  168. DateOnly? XpirDt,
  169. string? IssuCurCd,
  170. decimal? FirstIssuAmt,
  171. decimal? IssuRema,
  172. decimal? PayinAmt,
  173. decimal? CouponRate,
  174. decimal? XpiredRate,
  175. string? RecuWhcd,
  176. string? IssuWhcd,
  177. string? ParticulBondKindTpcd,
  178. string? OptionTpcd,
  179. bool? ForcErlyRedYn,
  180. string? MrChgTpcd,
  181. string? RegiOrgTpcd,
  182. string? GrtyTpcd,
  183. string? SignaTpcd,
  184. string? RankTpcd,
  185. string? IntPayWayTpcd,
  186. string? SintCintTpcd,
  187. string? IrateChgTpcd,
  188. decimal? XpirGuarPrate,
  189. string? XpirGuarPrateTpcd,
  190. string? PrcpRedWhcd,
  191. DateOnly? ApliDt,
  192. DateOnly? DlistDt,
  193. string? KisValatGrdCd,
  194. string? NiceValatGrdCd,
  195. string? SciValatGrdCd,
  196. string? KrValatGrdCd,
  197. bool? EltscYn,
  198. string? ExerMbodyTpcd,
  199. string? IntEstmMannTpcd
  200. );