using System.ComponentModel.DataAnnotations;
namespace Domain.Entities.Stocks;
///
/// SEIBro 주식관련사채 행사대상 주식정보 (getXrcStkStatInfo) — CB/BW/EB 행사대상·행사가·행사비율 (Wave 3).
/// 자연키 UNIQUE (BondIsin, XrcStkIsin) — 한 채권·행사주식 조합당 1행.
/// rolling per-채권ISIN — 발견 소스는 ConvertibleExercise(getXrcStkOptionXrcInfo) 가 반환한 DISTINCT BOND_ISIN.
/// ⚠️ getXrcStkStatInfo 실샘플이 result="0"(0행) 이라 필드 매핑은 주식.md 스펙 기준 — 운영 첫 수집 시 로그로 확인 필요.
///
public class ConvertibleBondTarget
{
[Key]
public int ID { get; private set; }
/// 채권 종목번호 (BOND_ISIN) — EB/BW/CB
public string BondIsin { get; private set; } = default!;
/// 채권 종목명 (BOND_SECN_NM)
public string? BondSecnNm { get; private set; }
/// 채권종류코드 (BOND_KIND_NM) — EB/CB/분리형BW/비분리형BW
public string? BondKindNm { get; private set; }
/// 행사주식종목번호 (XRC_STK_ISIN)
public string XrcStkIsin { get; private set; } = default!;
/// 주식 종목명 (STK_SECN_NM)
public string? StkSecnNm { get; private set; }
/// 신주인수권증권종목번호 (WRTB_ISIN)
public string? WrtbIsin { get; private set; }
/// 신주인수권증권명 (WRTB_SECN_NM)
public string? WrtbSecnNm { get; private set; }
/// 행사비율 (XRC_RATIO)
public decimal? XrcRatio { get; private set; }
/// 행사가격 (XRC_PRICE)
public decimal? XrcPrice { get; private set; }
/// 결산기말제한일수 (SETACC_ENDTERM_LIMIT_DAYS)
public int? SetaccEndtermLimitDays { get; private set; }
/// 신주배정일전제한일수 (NEWSTK_ALOC_DD_BF_LIMIT_DAYS)
public int? NewstkAllocDdBfLimitDays { get; private set; }
/// 청구제한사유내용 (DMAN_LIMIT_RSN_CONTENT)
public string? DmanLimitRsnContent { get; private set; }
public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow;
private ConvertibleBondTarget() { }
public static ConvertibleBondTarget Create(
string bondIsin,
string? bondSecnNm,
string? bondKindNm,
string xrcStkIsin,
string? stkSecnNm,
string? wrtbIsin,
string? wrtbSecnNm,
decimal? xrcRatio,
decimal? xrcPrice,
int? setaccEndtermLimitDays,
int? newstkAllocDdBfLimitDays,
string? dmanLimitRsnContent)
{
if (string.IsNullOrWhiteSpace(bondIsin))
{
throw new ArgumentException("bondIsin required", nameof(bondIsin));
}
if (string.IsNullOrWhiteSpace(xrcStkIsin))
{
throw new ArgumentException("xrcStkIsin required", nameof(xrcStkIsin));
}
return new ConvertibleBondTarget
{
BondIsin = bondIsin.Trim(),
BondSecnNm = Trimmed(bondSecnNm),
BondKindNm = Trimmed(bondKindNm),
XrcStkIsin = xrcStkIsin.Trim(),
StkSecnNm = Trimmed(stkSecnNm),
WrtbIsin = Trimmed(wrtbIsin),
WrtbSecnNm = Trimmed(wrtbSecnNm),
XrcRatio = xrcRatio,
XrcPrice = xrcPrice,
SetaccEndtermLimitDays = setaccEndtermLimitDays,
NewstkAllocDdBfLimitDays = newstkAllocDdBfLimitDays,
DmanLimitRsnContent = Trimmed(dmanLimitRsnContent)
};
}
/// 재수집 반영 — 변경 필드가 있을 때만 UpdatedAt 갱신 (자연키는 불변)
public void Update(
string? bondSecnNm,
string? bondKindNm,
string? stkSecnNm,
string? wrtbIsin,
string? wrtbSecnNm,
decimal? xrcRatio,
decimal? xrcPrice,
int? setaccEndtermLimitDays,
int? newstkAllocDdBfLimitDays,
string? dmanLimitRsnContent)
{
var changed = false;
changed |= Set(Trimmed(bondSecnNm), BondSecnNm, v => BondSecnNm = v);
changed |= Set(Trimmed(bondKindNm), BondKindNm, v => BondKindNm = v);
changed |= Set(Trimmed(stkSecnNm), StkSecnNm, v => StkSecnNm = v);
changed |= Set(Trimmed(wrtbIsin), WrtbIsin, v => WrtbIsin = v);
changed |= Set(Trimmed(wrtbSecnNm), WrtbSecnNm, v => WrtbSecnNm = v);
changed |= Set(xrcRatio, XrcRatio, v => XrcRatio = v);
changed |= Set(xrcPrice, XrcPrice, v => XrcPrice = v);
changed |= Set(setaccEndtermLimitDays, SetaccEndtermLimitDays, v => SetaccEndtermLimitDays = v);
changed |= Set(newstkAllocDdBfLimitDays, NewstkAllocDdBfLimitDays, v => NewstkAllocDdBfLimitDays = v);
changed |= Set(Trimmed(dmanLimitRsnContent), DmanLimitRsnContent, v => DmanLimitRsnContent = 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;
}
}