using System.ComponentModel.DataAnnotations;
using Domain.Entities.Stocks.ValueObject;
namespace Domain.Entities.Stocks;
///
/// 옵션(일반옵션/주식옵션유가/주식옵션코스닥) 일별매매 시세 (KRX OpenAPI drv 엔드포인트) — (OptionsKind, Code, TradeDate) UNIQUE.
/// 세 상품군을 한 테이블에 담고 OptionsKind 로 구분한다. 옵션은 선물과 달리 콜/풋(RGHT_TP_NM)·행사가(strike)·내재변동성(IMP_VOLT)을 갖는다:
/// - 가격/전일대비: TDD_CLSPRC/OPNPRC/HGPRC/LWPRC + CMPPREVDD_PRC. 미체결 시 종가/시고저는 "" → 0.
/// - 옵션 고유: RightType(RGHT_TP_NM Call/Put), StrikePrice(행사가 — 응답 필드 없이 ISU_NM 에서 파싱, nullable),
/// ImpliedVolatility(IMP_VOLT 내재변동성 %, nullable), NextDayBasePrice(NXTDD_BAS_PRC 익일정산가, nullable),
/// OpenInterest(ACC_OPNINT_QTY 미결제약정, nullable).
/// 행사가는 별도 필드가 없어 종목명에서 추출한다:
/// 지수옵션 "미니코스피 C 202501 232.5 (정규)" → 232.5, 주식옵션 "HD한국조선 C 202501 150,000( 10)" → 150000.
/// 종목코드(ISU_CD)는 8자리 영숫자(예: 205W1232) — 6자리 정규화 없이 원문 유지. 가격은 decimal(18,2).
///
public class OptionsDailyTrade
{
[Key]
public long ID { get; private set; }
/// 옵션 상품군 구분 (1=일반옵션, 2=주식옵션유가, 3=주식옵션코스닥)
public OptionsKind OptionsKind { get; private set; }
/// 종목코드 (ISU_CD) — 8자리 영숫자(예: 205W1232)
public string IsuCode { get; private set; } = default!;
/// 종목명 (ISU_NM)
public string IsuName { get; private set; } = default!;
/// 거래일 (BAS_DD)
public DateOnly TradeDate { get; private set; }
/// 권리유형 (RGHT_TP_NM — Call/Put)
public OptionRightType RightType { get; private set; }
/// 행사가 (ISU_NM 에서 파싱) — 추출 실패 시 null
public decimal? StrikePrice { get; private set; }
/// 종가 (TDD_CLSPRC) — 미체결 시 0
public decimal Close { get; private set; }
/// 시가 (TDD_OPNPRC) — 미체결 시 0
public decimal Open { get; private set; }
/// 고가 (TDD_HGPRC) — 미체결 시 0
public decimal High { get; private set; }
/// 저가 (TDD_LWPRC) — 미체결 시 0
public decimal Low { get; private set; }
/// 전일 대비 (CMPPREVDD_PRC) — 미제공("") 시 0
public decimal ChangeAmount { get; private set; }
/// 내재변동성 % (IMP_VOLT) — 미제공 시 null
public decimal? ImpliedVolatility { get; private set; }
/// 익일정산가 (NXTDD_BAS_PRC) — 미제공 시 null
public decimal? NextDayBasePrice { get; private set; }
/// 거래량 (ACC_TRDVOL)
public long Volume { get; private set; }
/// 거래대금 (ACC_TRDVAL, 원)
public long TradeValue { get; private set; }
/// 미결제약정 (ACC_OPNINT_QTY) — 미제공("") 시 null
public long? OpenInterest { get; private set; }
/// 상품구분 (PROD_NM 예: "미니코스피200 옵션")
public string? ProductName { get; private set; }
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
private OptionsDailyTrade() { }
public static OptionsDailyTrade Create(
OptionsKind optionsKind,
string isuCode,
string isuName,
DateOnly tradeDate,
OptionRightType rightType,
decimal? strikePrice,
decimal close,
decimal open,
decimal high,
decimal low,
decimal changeAmount,
decimal? impliedVolatility,
decimal? nextDayBasePrice,
long volume,
long tradeValue,
long? openInterest,
string? productName = null
) {
if (!Enum.IsDefined(optionsKind))
{
throw new ArgumentOutOfRangeException(nameof(optionsKind));
}
if (!Enum.IsDefined(rightType))
{
throw new ArgumentOutOfRangeException(nameof(rightType));
}
if (string.IsNullOrWhiteSpace(isuCode))
{
throw new ArgumentException("isuCode required", nameof(isuCode));
}
if (string.IsNullOrWhiteSpace(isuName))
{
throw new ArgumentException("isuName required", nameof(isuName));
}
return new OptionsDailyTrade
{
OptionsKind = optionsKind,
IsuCode = isuCode.Trim(),
IsuName = isuName.Trim(),
TradeDate = tradeDate,
RightType = rightType,
StrikePrice = strikePrice,
Close = close,
Open = open,
High = high,
Low = low,
ChangeAmount = changeAmount,
ImpliedVolatility = impliedVolatility,
NextDayBasePrice = nextDayBasePrice,
Volume = volume,
TradeValue = tradeValue,
OpenInterest = openInterest,
ProductName = productName
};
}
/// 동일 (OptionsKind, IsuCode, TradeDate) 재수집 시 값 갱신 (upsert)
public void Update(
string isuName,
OptionRightType rightType,
decimal? strikePrice,
decimal close,
decimal open,
decimal high,
decimal low,
decimal changeAmount,
decimal? impliedVolatility,
decimal? nextDayBasePrice,
long volume,
long tradeValue,
long? openInterest,
string? productName = null
) {
IsuName = isuName.Trim();
RightType = rightType;
StrikePrice = strikePrice;
Close = close;
Open = open;
High = high;
Low = low;
ChangeAmount = changeAmount;
ImpliedVolatility = impliedVolatility;
NextDayBasePrice = nextDayBasePrice;
Volume = volume;
TradeValue = tradeValue;
OpenInterest = openInterest;
ProductName = productName;
}
}