using System.ComponentModel.DataAnnotations;
namespace Domain.Entities.Stocks;
///
/// SEIBro 파생결합증권 기초자산 정보 (getAssetInfo) — 기초자산명·행사기준가·평가일 (Wave 5). 자연키 UNIQUE (Isin, AssetSeq).
/// rolling per-ISIN(DerivMaster 있는 것). 한 파생결합증권이 여러 기초자산(AssetSeq)을 가진다.
/// ⚠️ 표준기초자산코드(StndBassetCd)는 getBassetUnredScale 입력 13종(KSD02*)과 다른 체계(KSD31*/KR7*)로 반환될 수 있어 **원문 그대로 저장**(매핑 흡수 금지).
/// 소수 precision 은 넉넉히(파생결합증권.md 행사기준가 NUMBER(26,10)). Isin 은 응답에 있으나(가이드 누락분) 없으면 요청값 스탬핑.
///
public class DerivAsset
{
[Key]
public int ID { get; private set; }
/// 종목코드 (ISIN, 스탬핑) — getDerivCombiIsinInfo 연계
public string Isin { get; private set; } = default!;
/// 자산일련번호 (ASSET_SEQ)
public int AssetSeq { get; private set; }
/// 표준기초자산코드 (STND_BASSET_CD) — 원문 (KSD31*·KR7* 등 반환 체계 상이할 수 있음)
public string? StndBassetCd { get; private set; }
/// 표준기초자산명 (STND_BASSET_NM) — 예 Hang Seng China Enterprises Index
public string? StndBassetNm { get; private set; }
/// 행사기준가 (XRC_STDPRC)
public decimal? XrcStdprc { get; private set; }
/// 행사정지하한비율 (XRC_STOP_LOLMT_RATIO)
public decimal? XrcStopLolmtRatio { get; private set; }
/// 행사정지하한가격 (XRC_STOP_LOLMT_PRICE)
public decimal? XrcStopLolmtPrice { get; private set; }
/// 행사정지상한비율 (XRC_STOP_UPLMT_RATIO)
public decimal? XrcStopUplmtRatio { get; private set; }
/// 행사정지상한가격 (XRC_STOP_UPLMT_PRICE)
public decimal? XrcStopUplmtPrice { get; private set; }
/// 기초자산최초평가구분코드 (BASSET_FIRST_VALAT_TPCD) — 1종가 2산술평균 3장중가 9기타
public string? BassetFirstValatTpcd { get; private set; }
/// 기초자산최종평가구분코드 (BASSET_LAST_VALAT_TPCD)
public string? BassetLastValatTpcd { get; private set; }
/// 최초기준가결정시작일자 (FIRST_STDPRC_DECSN_BEGIN_DT)
public DateOnly? FirstStdprcDecsnBeginDt { get; private set; }
/// 최초기준가결정종료일자 (FIRST_STDPRC_DECSN_EXPRY_DT)
public DateOnly? FirstStdprcDecsnExpryDt { get; private set; }
/// 최종기준가결정시작일자 (LAST_STDPRC_DECSN_BEGIN_DT)
public DateOnly? LastStdprcDecsnBeginDt { get; private set; }
/// 최종기준가결정종료일자 (LAST_STDPRC_DECSN_EXPRY_DT)
public DateOnly? LastStdprcDecsnExpryDt { get; private set; }
/// 절상절사구분코드 (RVLT_SEVER_TPCD) — 1올림 2버림 3반올림 9해당없음
public string? RvltSeverTpcd { get; private set; }
public DateTime UpdatedAt { get; private set; } = DateTime.UtcNow;
private DerivAsset() { }
public static DerivAsset Create(string isin, int assetSeq, DerivAssetFields f)
{
if (string.IsNullOrWhiteSpace(isin))
{
throw new ArgumentException("isin required", nameof(isin));
}
var entity = new DerivAsset { Isin = isin.Trim(), AssetSeq = assetSeq };
entity.Apply(f);
return entity;
}
/// 재수집 반영 — 변경 필드가 있을 때만 UpdatedAt 갱신 (자연키 Isin/AssetSeq 은 불변)
public void Update(DerivAssetFields f)
{
Apply(f);
}
private void Apply(DerivAssetFields f)
{
var changed = false;
changed |= Set(Trimmed(f.StndBassetCd), StndBassetCd, v => StndBassetCd = v);
changed |= Set(Trimmed(f.StndBassetNm), StndBassetNm, v => StndBassetNm = v);
changed |= Set(f.XrcStdprc, XrcStdprc, v => XrcStdprc = v);
changed |= Set(f.XrcStopLolmtRatio, XrcStopLolmtRatio, v => XrcStopLolmtRatio = v);
changed |= Set(f.XrcStopLolmtPrice, XrcStopLolmtPrice, v => XrcStopLolmtPrice = v);
changed |= Set(f.XrcStopUplmtRatio, XrcStopUplmtRatio, v => XrcStopUplmtRatio = v);
changed |= Set(f.XrcStopUplmtPrice, XrcStopUplmtPrice, v => XrcStopUplmtPrice = v);
changed |= Set(Trimmed(f.BassetFirstValatTpcd), BassetFirstValatTpcd, v => BassetFirstValatTpcd = v);
changed |= Set(Trimmed(f.BassetLastValatTpcd), BassetLastValatTpcd, v => BassetLastValatTpcd = v);
changed |= Set(f.FirstStdprcDecsnBeginDt, FirstStdprcDecsnBeginDt, v => FirstStdprcDecsnBeginDt = v);
changed |= Set(f.FirstStdprcDecsnExpryDt, FirstStdprcDecsnExpryDt, v => FirstStdprcDecsnExpryDt = v);
changed |= Set(f.LastStdprcDecsnBeginDt, LastStdprcDecsnBeginDt, v => LastStdprcDecsnBeginDt = v);
changed |= Set(f.LastStdprcDecsnExpryDt, LastStdprcDecsnExpryDt, v => LastStdprcDecsnExpryDt = v);
changed |= Set(Trimmed(f.RvltSeverTpcd), RvltSeverTpcd, v => RvltSeverTpcd = 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;
}
}
/// DerivAsset 필드 묶음 (getAssetInfo — 자연키 AssetSeq 제외 필드)
public sealed record DerivAssetFields(
string? StndBassetCd,
string? StndBassetNm,
decimal? XrcStdprc,
decimal? XrcStopLolmtRatio,
decimal? XrcStopLolmtPrice,
decimal? XrcStopUplmtRatio,
decimal? XrcStopUplmtPrice,
string? BassetFirstValatTpcd,
string? BassetLastValatTpcd,
DateOnly? FirstStdprcDecsnBeginDt,
DateOnly? FirstStdprcDecsnExpryDt,
DateOnly? LastStdprcDecsnBeginDt,
DateOnly? LastStdprcDecsnExpryDt,
string? RvltSeverTpcd
);