using System.ComponentModel.DataAnnotations;
using Domain.Entities.Stocks.ValueObject;
namespace Domain.Entities.Stocks;
///
/// 일반상품(석유·금·배출권) 일별매매 시세 (KRX OpenAPI gen 엔드포인트) — (CommodityMarket, Code, TradeDate) UNIQUE.
/// 세 시장을 한 테이블에 담고 CommodityMarket 으로 구분한다. 시장별 응답 shape 이 다르다:
/// - 금(Gold)/배출권(Emission): ISU_CD/ISU_NM + 주식형 OHLC(TDD_CLSPRC/OPNPRC/HGPRC/LWPRC) + 전일대비(CMPPREVDD_PRC)·등락률(FLUC_RT).
/// - 석유(Oil): ISU_CD 없음 → OIL_NM(유종구분)을 Code·Name 으로 사용. OHLC 미제공(Open/High/Low=0), 전일대비·등락률 없음 → null.
/// 가격은 가중평균가격_경쟁(WT_AVG_PRC)을 Close 로, 가중평균가격_협의(WT_DIS_AVG_PRC)를 WeightedDiscussionPrice(석유 전용 nullable)로 담는다.
/// 공통 필드: Code/Name, TradeDate, Close/Open/High/Low(decimal 18,2), Volume/TradeValue(long). 등락은 금/배출권만, 협의가격은 석유만.
///
public class CommodityDailyTrade
{
[Key]
public long ID { get; private set; }
/// 일반상품 시장 구분 (1=석유, 2=금, 3=배출권)
public CommodityMarket CommodityMarket { get; private set; }
/// 단축코드 (ISU_CD) — 금/배출권. 석유는 OIL_NM(유종구분)을 코드로 사용
public string Code { get; private set; } = default!;
/// 종목명 (ISU_NM) — 석유는 OIL_NM(유종구분)
public string Name { get; private set; } = default!;
/// 거래일 (BAS_DD)
public DateOnly TradeDate { get; private set; }
/// 종가 (TDD_CLSPRC) — 석유는 가중평균가격_경쟁 (WT_AVG_PRC)
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) — 금/배출권만. 석유는 미제공 → null
public decimal? ChangeAmount { get; private set; }
/// 등락률 % (FLUC_RT) — 금/배출권만. 석유는 미제공 → null
public decimal? ChangeRate { get; private set; }
/// 가중평균가격_협의 (WT_DIS_AVG_PRC) — 석유만. 금/배출권은 null
public decimal? WeightedDiscussionPrice { get; private set; }
/// 거래량 (ACC_TRDVOL)
public long Volume { get; private set; }
/// 거래대금 (ACC_TRDVAL, 원)
public long TradeValue { get; private set; }
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
private CommodityDailyTrade() { }
public static CommodityDailyTrade Create(
CommodityMarket commodityMarket,
string code,
string name,
DateOnly tradeDate,
decimal close,
decimal open,
decimal high,
decimal low,
decimal? changeAmount,
decimal? changeRate,
decimal? weightedDiscussionPrice,
long volume,
long tradeValue
) {
if (!Enum.IsDefined(commodityMarket))
{
throw new ArgumentOutOfRangeException(nameof(commodityMarket));
}
if (string.IsNullOrWhiteSpace(code))
{
throw new ArgumentException("code required", nameof(code));
}
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("name required", nameof(name));
}
return new CommodityDailyTrade
{
CommodityMarket = commodityMarket,
Code = code.Trim(),
Name = name.Trim(),
TradeDate = tradeDate,
Close = close,
Open = open,
High = high,
Low = low,
ChangeAmount = changeAmount,
ChangeRate = changeRate,
WeightedDiscussionPrice = weightedDiscussionPrice,
Volume = volume,
TradeValue = tradeValue
};
}
/// 동일 (CommodityMarket, Code, TradeDate) 재수집 시 값 갱신 (upsert)
public void Update(
string name,
decimal close,
decimal open,
decimal high,
decimal low,
decimal? changeAmount,
decimal? changeRate,
decimal? weightedDiscussionPrice,
long volume,
long tradeValue
) {
Name = name.Trim();
Close = close;
Open = open;
High = high;
Low = low;
ChangeAmount = changeAmount;
ChangeRate = changeRate;
WeightedDiscussionPrice = weightedDiscussionPrice;
Volume = volume;
TradeValue = tradeValue;
}
}